Commit Graph
7 Commits
Author SHA1 Message Date
r4bbit cd8a84374b refactor(amm): extract swap-input formula into amm_core
Lift the inverse constant-product SwapExactOutput math out of the guest's
exact_output_swap_logic into amm_core::swap_exact_out_amounts(amount_out,
reserve_in, reserve_out, fee_bps) -> Option<(effective_in, required_in)>:
ceil(reserve_in * amount_out / (reserve_out - amount_out)) lifted through the
fee via mul_div_ceil. exact_output_swap_logic now calls it and keeps its
nonzero/exceeds-reserve asserts. Behavior-preserving (the panic-message tests
still pass); None (out >= reserve or zero fee multiplier) surfaces as the
existing expect.

Makes the on-chain exact-output pricing one reusable function so the
off-chain quote can produce byte-identical required-input figures instead of
re-deriving the inverse formula. Mirrors swap_exact_in_amounts.
2026-08-06 14:31:49 +02:00
r4bbit c3dc9dd94f refactor(amm): extract swap-output formula into amm_core
Lift the constant-product SwapExactInput math out of the guest's swap_logic
into amm_core::swap_exact_in_amounts(amount_in, reserve_in, reserve_out,
fee_bps) -> (effective_in, amount_out). swap_logic now calls it and keeps its
nonzero input/output asserts. Behavior-preserving (the two panic-message
tests still pass); the only change is that the impossible-overflow expects
become saturating.

Makes the on-chain pricing one reusable function so the off-chain swap quote
can produce byte-identical expected-output figures instead of re-deriving the
formula.
2026-08-05 22:54:37 +02:00
Ricardo Guilherme Schmidt cca063ce2d refactor(amm)!: select swap direction by input holding, sign only the input
Replace the `token_definition_id_in` argument with a role-based account
interface: swaps now take a `user_input_holding` and a `user_output_holding`
instead of positional token-A/token-B holdings. Direction is derived from the
input holding's own token definition, and the input slot is a framework-level
`#[account(signer)]` so authorization is enforced before execution rather than
delegated solely to the downstream token transfer. The output holding only
receives and needs no signature.

This removes the ambiguity the previous arg-based model carried (the arg could
disagree with the signed holding) and makes the IDL express the signing rule:
`user_input_holding` is `signer: true`, `user_output_holding` is `signer: false`.

The two user-holding post-states are echoed in the guest's declared slot order
(input, then output); the framework matches post-states to accounts by
position, so the internal A/B mapping used for reserve bookkeeping must not leak
into the returned order.

BREAKING CHANGE: The AMM swap instruction interface changed and the guest
ImageID/ProgramId changes as a result.
2026-07-21 09:17:29 +02:00
r4bbit 2308681dcf fix(amm): compute pool arithmetic in u256 to avoid u128 overflow
The AMM multiplied amounts in u128 — `token_a * token_b` for the initial
LP in `new_definition`, `reserve * amount` in swaps, and the mul/div steps
in add/remove liquidity. For realistic 18-decimal token amounts the
intermediate product exceeds `u128::MAX` (~3.4e38): opening a pool with
100/200 tokens is `1e20 * 2e20 = 2e40`, which panicked and caused the
sequencer to skip the transaction.

Widen the intermediate arithmetic, not the stored types. Add
`mul_div_floor`, `mul_div_ceil`, and `isqrt_product` to `amm_core` (using
`alloy_primitives::U256`, as `spot_price_q64_64` already does): they
compute the product/division/sqrt in U256 and downcast the result back to
u128. Route `new_definition`, `swap_exact_input`/`swap_exact_output`,
`add_liquidity`, and `remove_liquidity` through them. `swap_exact_output`
keeps its ceil rounding (required input rounded up, in the pool's favour)
via `mul_div_ceil`.

Balances, reserves, and LP supply stay u128, so account data formats,
IDLs, and the token/ata/stablecoin programs are unchanged. This lifts the
usable amount range to the full u128.
2026-06-30 15:13:47 +02:00
r4bbit 0e2c5f9329 feat(amm): refresh TWAP current tick on every reserve-mutating instruction
Make swap_exact_input, swap_exact_output, add_liquidity, remove_liquidity,
and sync_reserves keep the pool's TWAP current tick in sync with its
reserves. Each now takes the current-tick and clock accounts, reads the
TWAP program ID from the config account, validates the clock account and
the current-tick PDA, and after computing the post-op pool chains an
UpdateCurrentTick to the oracle carrying the post-op spot price, with the
pool passed as the authorized price source via its pool PDA seed.

sync_reserves additionally now takes the config account so it can resolve
the TWAP program ID and gate on initialization, consistent with the other
instructions.

The invariant current_tick == tick(reserves) therefore holds after every
operation. Proportional add/remove preserve the price, so the tick is
unchanged for them, but the refresh still runs and lands on the correct
value.
2026-06-22 12:45:55 +02:00
r4bbit 3624ea1451 feat(amm): add Initialize instruction with config-gated chained calls
Introduce a singleton AMM configuration account, a PDA derived from the
constant "CONFIG" seed, created once via a new `Initialize` instruction.
The config stores the Token Program ID the AMM issues every chained call
to, replacing the previous behavior of trusting the program owner of a
caller-supplied holding.

The config account's existence is the Program's initialization gate: the
chained-call instructions (new_definition, add_liquidity, remove_liquidity,
swap_exact_input, swap_exact_output) now take the config as their first
account, validate it against `compute_config_pda(self_program_id)`, and
read the Token Program ID from it on demand — rejecting calls until the
Program is initialized. Vaults and user holdings are asserted to match the
configured Token Program. sync_reserves is left ungated, as it cannot act
on a pool that could not have existed before initialization.

- amm_core: AmmConfig type, compute_config_pda/_seed, Initialize variant
- amm: initialize.rs + config threading through chained-call instructions
- guest: initialize instruction; config + self_program_id on gated calls
- tests: config fixtures, init-gate unit tests, end-to-end Initialize VM test
2026-06-18 16:11:28 +02:00
r4bbit 3622016e6c refactor: move programs into programs and UIs into apps
This refactors the repository structure as it has grown over time.
2026-05-26 14:05:52 +02:00