BaseHook reference
The Rust interface, authenticated context, dispatch rules and safe defaults for custom grapl hooks.
grapl_amm::hooks::base is the reusable authoring layer for executable hook programs. It uses the core lifecycle ABI and contains no application policy. The template tutorial shows how to use it.
Public interface
| Item | Contract |
|---|---|
BaseHook | Trait with capabilities, custom account validation and ten callback methods |
HookContext | Authenticated invocation, decoded pool and extra account views |
process_hook | Validate, dispatch, validate the reply and write canonical return data |
prepare_reply | Run the same validation and return encoded reply bytes without the return-data syscall |
authenticated_pool | Check core ownership, pool decoding/version and configured hook binding |
authenticated_invocation | Additionally check callback authority, privileges, pool address and entered state |
Use process_hook for callback entrypoints. The lower-level authentication helpers do not perform every dispatch check, including capability equality, sequence and mint matching. They are provided for compatibility with existing adapters.
BaseHook methods
| Method | Default behavior |
|---|---|
capabilities() | Required; return the exact callback/profile/permission contract |
validate_accounts(context) | Reject extra accounts and payload bytes |
before_initialize(context) | Reject unless implemented |
after_initialize(context) | Reject unless implemented |
before_add_liquidity(context) | Reject unless implemented |
after_add_liquidity(context) | Reject unless implemented |
before_remove_liquidity(context) | Reject unless implemented |
after_remove_liquidity(context) | Reject unless implemented |
before_swap(context) | Reject unless implemented |
after_swap(context) | Reject unless implemented |
before_donate(context) | Reject unless implemented |
after_donate(context) | Reject unless implemented |
A callback returns Result<HookReply>. context.acknowledge() returns the current ABI version and phase, allow = true, and no trading-fee or output-fee override. It does not execute transfers or emit return data itself. A DynamicFee BeforeSwap callback must set its required fee before returning.
The copyable template deliberately implements all methods as acknowledgements. The trait's default rejection prevents an enabled but forgotten callback from silently allowing an operation.
HookContext fields
| Field | Meaning |
|---|---|
invocation | Phase, pool, mints, actor, sequence, clock, snapshots, operation details, account descriptors and payload |
pool | Decoded core-owned pool authenticated against this hook program |
extra_accounts | Declared callback accounts after the authority and pool |
The dispatcher constructs this context after authentication. Its non-exhaustive structure prevents consumers from constructing one through the public Rust API. These are borrowed views; persist any required state in validated accounts owned by your program.
invocation.actor is attested by the core, not an independently forwarded wallet signature. A router PDA may itself be the actor. Read identity and authentication before implementing actor-dependent policy.
Dispatch checks
Before running custom code, the base checks:
- The executing program ID equals the expected hook ID.
- The invocation's tag, ABI version, encoding and phase-specific context are valid.
- The authority is the core-derived
[b"hook", pool]PDA and is a read-only signer; the pool is read-only and not a signer. - The pool is a non-executable account owned by the core, with a valid discriminator/version, bound to this hook, and marked as entered.
- The pool address, mint pair and operation sequence match the authenticated state.
- Extra account count matches the descriptors, the phase is enabled, and capabilities exactly match the pool's bitmap, profile and permissions.
- Your
validate_accountsimplementation accepts the custom state and payload.
After dispatch, the base validates the reply against the phase, profile, fee cap and output-fee permissions, then encodes it. process_hook writes it to Solana return data. The core independently validates the response and its originating program.
Responsibilities that remain with the author
Validate policy-specific PDAs, account relationships, external program identities and all arithmetic. The core's generic account classification does not prove that an account is the reward recipient or configuration your application intended.
Configure setup and upgrade authorities deliberately. This interface does not deploy your program, create policy state, implement administrative instructions, or establish a quote adapter. It also does not make pool hooks run on arbitrary token transfers outside the pool.
The lifecycle probe uses the same base and rejects selected phases to test rollback. Test custom side effects and economic invariants in addition to that generic harness. See validation and ABI reference.