Repository navigation
Payments 1/8: Fix VaultRouter <-> tier-vault ABI mismatch and unify asset-scoped accounting #138
Description
Problem / Context
VaultRouter (contracts/vault_router/src/lib.rs) forwards deposit, withdraw, and early_exit to each tier vault via env.invoke_contract using a 3-argument, asset-scoped signature: (user, asset, amount) (see lines 150-207). But the tier vaults do not implement that interface:
VaultL3::deposit/withdraw/early_exit(contracts/vault_l3/src/lib.rs) take only(user, amount)— 2 arguments, noassetparameter, and store balances in a single globalBalance(Address)key (USDC-only accounting).VaultFlex::withdraw(contracts/vault_flex/src/lib.rsline 61) is worse: its second parameter isstrategy_balance: i128, not an amount at all — it's the external strategy balance used for pro-rata yield math — and the function always performs a full withdrawal (there is no partial-withdrawal branch, unlikeVaultL3).
This is a hard ABI mismatch: Soroban's env.invoke_contract requires the argument vector to match the target function's exported signature exactly. As currently written, VaultRouter's deposit/withdraw/early_exit calls against real (non-mock) tier vault instances will fail. This hasn't surfaced yet because vault_router's own unit tests exercise a hand-written MockVault with a compatible 3-arg signature (see contracts/vault_router/src/lib.rs test module, lines 354-369), and nothing is deployed yet — deployments/testnet.json and mainnet.json are still placeholder files.
Objective
Establish one consistent, asset-scoped payment-initiation interface across all four tier vaults (VaultFlex, VaultL3, VaultL6, VaultL12) that actually matches what VaultRouter calls, so a deposit/withdraw/early-exit transaction can succeed end-to-end against the real contracts.
Technical approach
- Change all tier vaults' storage keys to be asset-scoped:
Balance(Address, Address),Shares(Address, Address),LockUntil(Address, Address),Checkpoint(Address, Address), keyed by(user, asset). - Standardize
deposit(user, asset, amount),withdraw(user, asset, amount) -> i128,early_exit(user, asset, amount) -> i128across all four vaults. - Rewrite
VaultFlex::withdrawto accept a realamountand support partial withdrawal exactly likeVaultL3already does (full-vs-partial branch, dust handling), instead of depending on an externally-suppliedstrategy_balance. Decide how Flex sources the strategy balance it needs for yield math (owntotal_balancebookkeeping vs. a callback intoStrategyVault). - Ensure min-deposit checks, TVL caps, exit-fee math, and share-checkpoint logic (
shared::checkpoint) are applied per-asset, not globally. - Add integration tests that exercise
VaultRouteragainst the real vault contracts (notMockVault) for deposit/withdraw/early_exit across at least two allowlisted assets, so interface drift like this is caught by CI going forward.
Detailed scope
contracts/vault_flex, contracts/vault_l3, contracts/vault_l6, contracts/vault_l12, contracts/shared/src/checkpoint.rs (if per-asset checkpoints require changes), and new integration tests in contracts/vault_router (or a new integration-test crate) that replace/augment the mock-only test harness.
Important edge cases and failure scenarios
- Two different assets deposited by the same user in the same tier must not cross-contaminate share/lock accounting.
- Partial withdraw/early-exit leaving a dust balance below the tier's minimum deposit — decide whether dust is allowed to remain or must be swept on partial exit.
VaultL3's existing emergency-unlock flag must apply per-asset once accounting is asset-scoped, not globally freeze/unfreeze every asset in the tier.- Multiplier and fee math must not double-apply now that storage keys include the asset dimension.
VaultFlex's rewritten withdraw must preserve the audited fixed-point yield-distribution behavior it currently gets fromstrategy_balance— verify no precision regression versus the current single-asset math.
Dependencies
None — this is the foundation issue for the payment track; all following issues assume a working, consistent contract interface.
Acceptance criteria
VaultRouter.deposit/withdraw/early_exitsucceed end-to-end against real (non-mock) instances of all four tier vaults, for at least two allowlisted assets, in an automated test.VaultFlexsupports partial withdrawal with the same semantics asVaultL3/VaultL6/VaultL12.- All four tier vaults expose identical function signatures for
deposit/withdraw/early_exit/balance/shares/lock_until. cargo testpasses across the full workspace, including new cross-contract integration tests.
Definition of done
PR merged with the updated contracts, new integration tests, and a CHANGELOG.md entry; CI (cargo build / cargo test) green; no remaining 2-arg-vs-3-arg (or otherwise mismatched) signatures between VaultRouter and any tier vault.
Activity
Peculiarr7 commentedon Aug 18, 2026
Hi! I’m a full-stack developer with a strong blockchain background, and I’m confident I can resolve this issue quickly and effectively. I’ll deliver a clean, reliable solution with immediate attention to detail, ensuring it’s completed as soon as possible. Please assign it to me—I’m ready to get started right away.
DavidAkere204 commentedon Aug 18, 2026
I would like to work on this issue
soundsng commentedon Aug 19, 2026
Looked through this project and understood its current implementation, architecture, and overall stage of maturity.
grantfox-oss commentedon Aug 19, 2026
🦊 GrantFox — @soundsng has been assigned to this issue as part of the Third Campaign campaign!
Next steps:
Closes #138)Good luck! Track your progress on GrantFox.
grantfox-oss commentedon Aug 21, 2026
🎉 This issue has been marked as completed on GrantFox as part of the Third Campaign campaign!
@soundsng's PR #146 was approved and merged by @wumibals.
🏆 @soundsng: You earned 40 FoxPoints for this contribution! Your current tier: Builder (1,425 total points). Track your full progress on GrantFox.
👏 Great work, @soundsng! Keep contributing to LadderMine.
fix(contracts): unify VaultRouter <-> tier-vault ABI and asset-scope …