Investigate: queue bridge txs when amount exceeds withdraw rate limits (per-tx and daily window) #132

Open
opened 2026-05-19 08:10:25 +00:00 by PlasticDigits · 0 comments
PlasticDigits commented 2026-05-19 08:10:25 +00:00 (Migrated from gitlab.com)

Summary

Investigate user-initiated queuing when a bridge transfer (deposit and/or destination withdraw execute) would exceed destination withdraw rate limits:

  1. Per-transaction cap (maxPerTransaction / max_per_tx) — payout larger than a single allowed transfer.
  2. Per-period cap (maxPerPeriod / max_per_period, 24h rolling window) — payout fits per-tx but remaining window headroom is insufficient, or cumulative usage would exceed the daily cap.

Today the UI blocks or warns rather than offering a queue: users must wait for the window to reset, split into smaller transfers manually, or abandon the transfer.

Current behavior (reference)

Surface Behavior
Transfer form MAX / CTA gated by destination withdrawRateLimit.remainingAmount, minPerTransaction, bridge caps (TransferForm.tsx, INV-UX1 GL-119).
Transfer Status (approved, not executed) INV-UX2 (GL-127): temporarily-blocked shows amber countdown; permanently-blocked when normalized payout > maxPerPeriod (EVM: computeEvmExecutionRateLimitStatus; Terra: queryTerraRateLimitStatus, INV-UX2-TERRA1 GL-130).
On-chain EVM TokenRateLimit, Terra RATE_LIMITS + period_usage, Solana WithdrawRateLimit PDA enforce at withdraw execute (not at deposit). See docs/security-model.md, docs/FRONTEND_BRIDGE_INVARIANTS.md.

Related issues: #127 (rate-limit UX visibility), #130 (Terra decimal parity for permanent block classification).

Investigation goals

Product / UX

  • Should queuing apply at submit deposit time, at withdraw execute time, or both?
  • For per-tx exceedance: auto-split into N chained transfers vs single queued intent vs reject with guidance?
  • For daily window: queue until periodEndsAt / headroom available — show position, ETA, cancel?
  • Cross-chain parity: EVM, Terra, Solana — same semantics and copy?

Technical options (non-exhaustive)

Layer Options to evaluate
Client-only Persist “pending queue” in local storage / backend; poll rate-limit window; prompt user to sign execute when unblocked (no protocol change).
Operator Operator-side queue (cf. existing operator queues metrics in packages/operator) to retry withdraw_approve / execute when limits allow — user still signs deposit once.
Protocol New on-chain “queued withdraw” state — higher scope; security review required.

Edge cases

  • Amount exceeds maxPerPeriod and maxPerTransaction (currently permanently-blocked in UI) — can any queue help, or only split/sub-cap transfers?
  • Fee / cancel window expiry while queued.
  • Multiple queued txs for same token — FIFO vs aggregate headroom reservation.
  • Decimal normalization (normalizeBridgeAmountToDestDecimals) must match execute-time checks on all chains.

Acceptance criteria (investigation phase)

  • Written recommendation: client vs operator vs protocol, with effort/risk table.
  • Explicit handling for per-tx vs per-period limits (including permanent-block cases).
  • Parity checklist for EVM / Terra / Solana.
  • If proceeding: spike issue(s) for implementation with test plan (unit + e2e where applicable).

Code pointers

  • packages/frontend/src/services/evmExecutionRateLimit.ts
  • packages/frontend/src/services/terraBridgeQueries.ts (queryTerraRateLimitStatus)
  • packages/frontend/src/components/transfer/TransferForm.tsx
  • packages/frontend/src/pages/TransferStatusPage.tsx
  • docs/FRONTEND_BRIDGE_INVARIANTS.md (INV-UX2)
  • packages/contracts-solana/programs/cl8y-bridge/src/rate_limit.rs
## Summary Investigate **user-initiated queuing** when a bridge transfer (deposit and/or destination withdraw execute) would exceed destination **withdraw rate limits**: 1. **Per-transaction cap** (`maxPerTransaction` / `max_per_tx`) — payout larger than a single allowed transfer. 2. **Per-period cap** (`maxPerPeriod` / `max_per_period`, 24h rolling window) — payout fits per-tx but **remaining window headroom** is insufficient, or cumulative usage would exceed the daily cap. Today the UI **blocks or warns** rather than offering a queue: users must wait for the window to reset, split into smaller transfers manually, or abandon the transfer. ## Current behavior (reference) | Surface | Behavior | |---------|----------| | **Transfer form** | MAX / CTA gated by destination `withdrawRateLimit.remainingAmount`, `minPerTransaction`, bridge caps (`TransferForm.tsx`, INV-UX1 GL-119). | | **Transfer Status (approved, not executed)** | INV-UX2 (GL-127): `temporarily-blocked` shows amber countdown; `permanently-blocked` when normalized payout > `maxPerPeriod` (EVM: `computeEvmExecutionRateLimitStatus`; Terra: `queryTerraRateLimitStatus`, INV-UX2-TERRA1 GL-130). | | **On-chain** | EVM `TokenRateLimit`, Terra `RATE_LIMITS` + `period_usage`, Solana `WithdrawRateLimit` PDA enforce at **withdraw execute** (not at deposit). See `docs/security-model.md`, `docs/FRONTEND_BRIDGE_INVARIANTS.md`. | **Related issues:** #127 (rate-limit UX visibility), #130 (Terra decimal parity for permanent block classification). ## Investigation goals ### Product / UX - Should queuing apply at **submit deposit** time, at **withdraw execute** time, or both? - For **per-tx exceedance**: auto-split into N chained transfers vs single queued intent vs reject with guidance? - For **daily window**: queue until `periodEndsAt` / headroom available — show position, ETA, cancel? - Cross-chain parity: EVM, Terra, Solana — same semantics and copy? ### Technical options (non-exhaustive) | Layer | Options to evaluate | |-------|---------------------| | **Client-only** | Persist “pending queue” in local storage / backend; poll rate-limit window; prompt user to sign execute when unblocked (no protocol change). | | **Operator** | Operator-side queue (cf. existing operator `queues` metrics in `packages/operator`) to retry `withdraw_approve` / execute when limits allow — user still signs deposit once. | | **Protocol** | New on-chain “queued withdraw” state — higher scope; security review required. | ### Edge cases - Amount exceeds `maxPerPeriod` **and** `maxPerTransaction` (currently **permanently-blocked** in UI) — can any queue help, or only split/sub-cap transfers? - Fee / cancel window expiry while queued. - Multiple queued txs for same token — FIFO vs aggregate headroom reservation. - Decimal normalization (`normalizeBridgeAmountToDestDecimals`) must match execute-time checks on all chains. ## Acceptance criteria (investigation phase) - [ ] Written recommendation: client vs operator vs protocol, with effort/risk table. - [ ] Explicit handling for **per-tx** vs **per-period** limits (including permanent-block cases). - [ ] Parity checklist for EVM / Terra / Solana. - [ ] If proceeding: spike issue(s) for implementation with test plan (unit + e2e where applicable). ## Code pointers - `packages/frontend/src/services/evmExecutionRateLimit.ts` - `packages/frontend/src/services/terraBridgeQueries.ts` (`queryTerraRateLimitStatus`) - `packages/frontend/src/components/transfer/TransferForm.tsx` - `packages/frontend/src/pages/TransferStatusPage.tsx` - `docs/FRONTEND_BRIDGE_INVARIANTS.md` (INV-UX2) - `packages/contracts-solana/programs/cl8y-bridge/src/rate_limit.rs`
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
code/cl8y-bridge-monorepo#132
No description provided.