Instruction Reference
A systematic reference for all five instructions on the on-chain program.
docs/INTEGRATION_GUIDE.md covers how to integrate; this covers exactly
what each instruction does, requires, and can return -- useful when you're
past the quick-start and need to know a specific account constraint or
error condition. Everything here is read directly from
programs/inertia-protocol/src/instructions/, not from the SDK's wrapper.
Timing, in one place
Every threshold is a slot count, checked against Clock::get()?.slot, never
wall-clock time:
| Constant | Value | Meaning |
|---|---|---|
TTL_SLOTS |
2 (~800ms) | Past this, execute_swap becomes a rescue attempt instead of an ordinary one |
TIP_DECAY_SLOTS |
15 (~6s) | How long the anti-snipe required tip takes to decay from "equals the reward" back to the normal floor |
SELF_RESCUE_SLOTS |
150 (~60s) | Past this, user_wallet can reclaim the buffer directly via self_rescue |
CLEANUP_SLOTS |
300 (~120s) | Past this, anyone can close an untouched escrow via cleanup_expired_escrow |
Buffer split on a genuine rescue: 90% keeper (KEEPER_SHARE_BPS) / 5%
partner (PARTNER_SHARE_BPS) / 5% treasury (TREASURY_SHARE_BPS).
cleanup_expired_escrow's bounty is 10% (CLEANUP_BOUNTY_BPS) of the
untouched buffer, with the remaining 90% plus rent going back to the user.
initialize_escrow
Who calls it: user_wallet, as a real signer.
Escrow status after: Pending.
Transfers gas_buffer_lamports from user_wallet into a newly created
escrow PDA, and delegates SPL Token spending authority over exactly
input_amount of user_input_token_account to that PDA (token::approve
-- tokens never leave the user's own account; the user can revoke this
independently of Inertia at any time). Both user_input_token_account and
expected_destination_token_account are verified on-chain to be owned by
user_wallet -- a buggy or malicious platform SDK cannot point either
anywhere else.
| Param | Meaning |
|---|---|
nonce |
Client-generated, makes the escrow PDA unique per (user_wallet, nonce). Not a signature, not stored as proof of anything. |
gas_buffer_lamports |
The actual bounty/refund buffer posted. |
dynamic_minimum_lamports |
A floor you compute off-chain from real-time priority fees; the contract only checks gas_buffer_lamports >= dynamic_minimum_lamports. Both values come from the same caller -- this is a guard against your own SDK under-funding an escrow, not an adversarial check. |
partner_wallet |
Gets 5% of the buffer on a genuine rescue. |
input_amount |
Amount delegated for the swap. |
expected_program_id |
The program execute_swap's CPI must target. |
expected_discriminator |
That program's swap instruction's 8-byte Anchor discriminator. |
expected_output_amount |
Slippage floor, checked after the swap lands. |
Errors it can raise: BufferBelowMinimum, UnauthorizedUser (either
token account not owned by user_wallet).
execute_swap
Who calls it: anyone, at any time while Pending -- permissionless by
design. Behavior branches on elapsed time, not caller identity.
Escrow status after: Executed.
- Before
TTL_SLOTS: an ordinary attempt. On success, 100% of the buffer refunds touser_walletregardless of who called it -- there's nothing in it for a keeper to call this early, which is what makes this branch self-defending without needing a caller check. - After
TTL_SLOTS: a genuine rescue. Requires a Jito tip instruction in the same transaction, in an amount that starts equal to the keeper's own reward at the earliest eligible slot and decays linearly toMIN_JITO_TIP_LAMPORTSoverTIP_DECAY_SLOTS(see the ANTI-SNIPE design note in the source, anddocs/RISK_REGISTER.mdfor the known residual griefing risk this doesn't fully close). On success, the buffer splits 90/5/5 (caller/partner/treasury) -- payment goes straight to whichever key actually signed, since there's no separate stored "keeper" field to redirect.
Takes the entire CPI account list from the caller via remainingAccounts,
in whatever order the target program actually needs (see
docs/INTEGRATION_GUIDE.md for the full pattern). Three accounts must each
appear somewhere in that list -- the escrow's real
user_input_token_account, its real expected_destination_token_account,
and the real SPL Token program -- and the escrow's own entry has its
signer flag forced to true internally wherever it appears, since only
invoke_signed's PDA seeds can actually grant that.
After the CPI, the destination token account's balance increase is checked
against expected_output_amount before any funds move.
Errors it can raise: NotPending, InvalidSwapInstructionData (missing
or wrong 8-byte discriminator prefix), MissingRequiredSwapAccount (one of
the three required accounts absent from remainingAccounts),
MissingJitoTip / tip-amount check failures on the rescue path,
OutputBelowMinimum, Overflow.
self_rescue
Who calls it: user_wallet only, as a real signer -- the one
instruction where caller identity is checked.
Escrow status after: Rescued.
Past SELF_RESCUE_SLOTS with nothing resolved, the user can revoke the
token delegation directly (SPL Token's Revoke requires the token
account's actual owner, which the escrow PDA is not) and close the escrow,
sweeping both the untouched gas buffer and the rent-exempt reserve back to
themselves in one transfer. This is the guaranteed fallback -- it works
regardless of delegation state, keeper availability, or DEX liquidity.
Errors it can raise: UnauthorizedUser, NotPending,
SelfRescueWindowNotElapsed.
cleanup_expired_escrow
Who calls it: anyone -- permissionless, including a keeper wanting the
bounty, the platform, or an unrelated third party.
Escrow status after: Expired.
Covers the case where nobody acted at all -- not a keeper, not even the
user via self_rescue. Past CLEANUP_SLOTS, any caller can close the
escrow, earning a 10% bounty from the untouched buffer; the remaining 90%
plus rent goes back to user_wallet. Cannot revoke the token delegation
(same reason execute_swap can't -- only the actual token owner can), so
the user retains that cleanup themselves whenever they choose. This exists
purely to stop abandoned escrow accounts from accumulating indefinitely,
with a real economic incentive for someone to bother.
Errors it can raise: NotPending, CleanupWindowNotElapsed.
top_up_buffer
Who calls it: anyone -- user, platform, or a concerned third party --
while the escrow is Pending.
Escrow status after: unchanged (Pending).
Adds amount lamports to the existing buffer. Exists for the case where
priority fees spike after an escrow was created and the original buffer is
no longer enough to attract a keeper -- rather than the escrow being stuck
underfunded until it expires.
Errors it can raise: NotPending, Overflow.
Full error reference
| Error | Raised by | Meaning |
|---|---|---|
BufferBelowMinimum |
initialize_escrow |
gas_buffer_lamports < dynamic_minimum_lamports |
NotPending |
all instructions except initialize_escrow |
Escrow isn't in Pending status (already executed, rescued, expired, or doesn't exist) |
TtlNotElapsed |
(reserved) | |
SelfRescueWindowNotElapsed |
self_rescue |
Called before SELF_RESCUE_SLOTS have elapsed |
CleanupWindowNotElapsed |
cleanup_expired_escrow |
Called before CLEANUP_SLOTS have elapsed |
MissingJitoTip |
execute_swap |
Rescue attempt with no Jito tip instruction in the transaction |
MissingSwapInstruction |
(reserved) | |
SwapDestinationMismatch |
(reserved) | |
KeeperSignerMismatch |
(reserved) | |
UnauthorizedUser |
initialize_escrow, self_rescue |
Caller or a supplied token account doesn't match the escrow's real owner |
Overflow |
execute_swap, top_up_buffer |
Arithmetic overflow in a checked add/sub |
FeeSplitMismatch |
(reserved) | |
InvalidSwapInstructionData |
execute_swap |
Swap instruction data missing or its first 8 bytes don't match expected_discriminator |
OutputBelowMinimum |
execute_swap |
Swap CPI succeeded but returned less than expected_output_amount |
MissingRequiredSwapAccount |
execute_swap |
Input token account, destination, or the real SPL Token program absent from remainingAccounts |
A few variants are reserved for related checks folded into the
tip-amount/decay logic rather than raised as their own distinct paths in
the current implementation; listed for completeness against the full enum
in errors.rs.