fix(mock-amm): add withdraw_liquidity so seeded liquidity is recoverable - #58
Merged
Junman140 merged 2 commits intoJul 27, 2026
Merged
Conversation
deposit_liquidity moved token_out into the pool with no counterpart, so any liquidity seeded for testnet swaps was permanently locked. Add an admin-gated withdraw_liquidity(to, amount) that transfers pooled token_out back out. Non-positive amounts return the new InvalidAmount; withdrawing more than the pool holds returns the existing InsufficientLiquidity, leaving balances untouched in both cases. The admin is authenticated from stored state rather than accepted as an argument, per the convention documented in wpi-token's require_admin. Tests cover the deposit -> withdraw round trip (including a partial withdraw), recovery of the remainder after a swap, withdrawal to a non-admin recipient, both rejection paths, and that a non-admin signer cannot satisfy require_auth. The existing swap test is refactored onto the shared setup helper the new tests use. Fixes Pi-Defi-world#8 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
📝 WalkthroughWalkthroughMockAmm adds an admin-authorized ChangesMock AMM liquidity management
wPi-token maintenance
Estimated code review effort: 3 (Moderate) | ~25 minutes Sequence Diagram(s)sequenceDiagram
participant Admin
participant MockAmm
participant TokenContract
participant Recipient
Admin->>MockAmm: withdraw_liquidity(to, amount)
MockAmm->>TokenContract: check token_out balance
MockAmm->>TokenContract: transfer token_out
TokenContract-->>Recipient: receive withdrawn tokens
Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
The contracts job has been failing on main since Pi-Defi-world#57, on three counts: - cargo fmt --check reported diffs in lib.rs (remove_proposed_admin) and test.rs (trailing whitespace, an unwrapped mock_auths chain). - test.rs called client.volume_limit_config().unwrap(), but the generated client already unwraps, so VolumeLimitConfig has no unwrap and the test target did not compile (E0599). - 14 `admin` bindings left unused after Pi-Defi-world#55 removed the admin argument, each fatal under clippy's -D warnings. Apply rustfmt, drop the stray unwrap, and prefix the unused bindings with an underscore. No contract behaviour or test assertion changes. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #8
Problem
deposit_liquiditymovestoken_outinto the pool, but there was no counterpart, so any liquidity seeded for testnet swaps was permanently locked in the contract.Change
Adds
withdraw_liquidity(to, amount), gated to the pool admin:InvalidAmounterror (= 4)InsufficientLiquidityNo LP-share accounting — this is a testnet mock, so seeded liquidity is recoverable only by the pool admin. If the AMM ever becomes more than a mock, that's the point to revisit.
Note on the signature
The issue proposed
withdraw_liquidity(admin, to, amount). This implementation drops theadminargument and authenticates from stored state instead.That's deliberate: accepting an admin address as an argument is explicitly ruled out in this codebase. See the comment on
require_admininwpi-token/src/lib.rs— "Callers must never accept an admin address as an argument … doing so couples correctness to argument order and lets a caller-supplied address silently stand in for the real admin" — added in7bfb3a6, which removed that pattern fromwpi-tokenfor exactly this reason. Writing new privileged code in the pattern the repo just finished eradicating seemed like the wrong trade.Both acceptance criteria are still met. Happy to switch to the literal signature from the issue if maintainers prefer parity — it's a small change, and I'd rather settle it in review than assume.
upgrade()still takes anadminargument (it compares against stored admin, so it is safe, just older style). Left alone as out of scope.Also included: a CI fix (
eef6676)The
contractsjob has been failing onmainsince #57, unrelated to this change. Since it would have shown up as a red X on this PR, it's fixed here, isolated in its own commit:cargo fmt --checkdiffs inwpi-token/src/lib.rs(remove_proposed_admin) andtest.rs(trailing whitespace, an unwrappedmock_authschain)test.rs:470calledclient.volume_limit_config().unwrap(), but the generated client already unwraps, soVolumeLimitConfighas nounwrap— a hard compile error (E0599) that meantcargo testnever ran at alladminbindings left unused after fix(wpi-token): authenticate admin from stored state, not caller-supplied argument #55 removed the admin argument, each fatal under clippy's-D warningsNo contract behaviour or test assertions were changed. Happy to split this into its own PR if you'd rather review it separately — it cherry-picks cleanly.
Tests
Seven in
mock-amm, all passing. The existing swap test is refactored onto a sharedsetuphelper that the new tests reuse.deposit_and_withdraw_liquidity_round_tripwithdraw_liquidity_recovers_the_remainder_after_a_swapwithdraw_liquidity_can_send_to_an_address_other_than_the_adminwithdraw_liquidity_rejects_more_than_the_pool_holdsInsufficientLiquidity, balance unchangedwithdraw_liquidity_rejects_non_positive_amountsInvalidAmountfor0and-1, balance unchangednon_admin_signer_cannot_authenticate_withdraw_liquidityrequire_authswaps_against_registered_stellar_asset_contractVerified locally against every gate the
contractsjob runs:cargo fmt --all -- --checkcargo clippy --locked --all-targets --all-features -- -D warningscargo test --locked --allcargo build --locked --target wasm32-unknown-unknown --releaseverify_dependency_provenance.shNo WASM baseline update needed —
mock_ammis still0inwasm-size-baseline.json, so the size gate reports "no baseline" and skips.Out of scope
token_in(wPi) accumulated in the pool from swaps is still permanently locked. Same class of bug as this issue, but not covered by #8 — worth a follow-up issue.