Skip to content

Instantly share code, notes, and snippets.

@BenItBuhner
Created June 3, 2026 05:17
Show Gist options
  • Select an option

  • Save BenItBuhner/e8dca9df46ff27ec10f7702c5f8f4f24 to your computer and use it in GitHub Desktop.

Select an option

Save BenItBuhner/e8dca9df46ff27ec10f7702c5f8f4f24 to your computer and use it in GitHub Desktop.
AIBTC mpwizl08f7b54c2ff179 Bitflow DLMM swap router v1-1 static analysis

Bitflow dlmm-swap-router-v-1-1 Static Analysis

Scope

Contract: SM1FKXGNZJWSTWDWXQZJNF7B5TV5ZB235JTCXYXKD.dlmm-swap-router-v-1-1

Reviewed source: https://api.hiro.so/v2/contracts/source/SM1FKXGNZJWSTWDWXQZJNF7B5TV5ZB235JTCXYXKD/dlmm-swap-router-v-1-1

Source SHA-256: 304b654107a5012dc49ae05c617185d9746d963ae60445be281ab7f47335f1e4

Referenced interfaces and core contract were reviewed to understand the router's external effects:

  • SM1FKXGNZJWSTWDWXQZJNF7B5TV5ZB235JTCXYXKD.dlmm-pool-trait-v-1-1
  • SM1FKXGNZJWSTWDWXQZJNF7B5TV5ZB235JTCXYXKD.sip-010-trait-ft-standard-v-1-1
  • SP1PFR4V08H1RAZXREBGFFQ59WB739XM8VVGTFSEA.dlmm-core-v-1-1

This is a static analysis of the deployed router. It does not claim a dynamic exploit, and it found no high- or critical-severity issue requiring private disclosure before publication.

1. State Model

The router is stateless. It defines no data-var and no data-map.

Constants

Lines Constant Purpose
9-19 ERR_* Router-specific response errors for missing fold data, slippage, minimum receive values, empty paths, list bounds, bin bounds, and step bounds.
22-23 MIN_BIN_ID, MAX_BIN_ID Bounds caller-provided expected bin IDs to -500..500.
26-27 MIN_STEPS, MAX_STEPS Bounds range-based traversal to 1..319 calls.
30-44 STEP_INDEX_RANGE Static iterator list used to drive repeated traversal of the pool's current active bin.

External state mutated through the core

The router delegates swap execution to fixed core contract SP1PFR4V08H1RAZXREBGFFQ59WB739XM8VVGTFSEA.dlmm-core-v-1-1. The core validates that token traits match the pool and that the pool is managed by that core before moving tokens. Successful swaps can:

  • transfer SIP-010 input tokens from tx-sender to the selected pool;
  • transfer SIP-010 output tokens from the pool to tx-sender;
  • update the selected pool's bin balances;
  • advance the pool's active bin ID when a bin is depleted; and
  • accrue protocol fees in the core's unclaimed-protocol-fees map.

All router assertions and downstream try! failures revert the transaction atomically.

2. Function Inventory

Every public router function is open to any caller. The router has no owner, admin, pause switch, or privileged principal. Downstream pool validity and token trait checks occur in the fixed core.

Lines Function Preconditions and checks External calls and state effects
48-59 swap-multi Non-empty list after fold; each expected bin ID within bounds; each hop meets its own min-received; cumulative unfavorable-bin distance is at most max-unfavorable-bins. Each hop reads pool active bin and invokes fixed core swap-x-for-y or swap-y-for-x.
62-77 swap-x-for-y-same-multi Non-empty list after fold; bounded expected bin IDs; per-hop minimum; cumulative unfavorable-bin distance cap; aggregate Y output at least min-y-amount-total. Repeatedly sends remaining X input through fixed-core X-for-Y swaps until input is exhausted or the pool list ends.
80-95 swap-y-for-x-same-multi Symmetric to X-for-Y: non-empty list, bounded expected bin IDs, per-hop minimum, unfavorable-bin cap, aggregate X output floor. Repeatedly sends remaining Y input through fixed-core Y-for-X swaps.
98-107 swap-simple-multi Non-empty list after fold; at most five caller-specified pool legs; each leg validates max-steps and its minimum output. Runs a range-based traversal for each leg and returns one {in, out} result per leg.
110-116 swap-x-for-y-simple-multi Wrapper using MAX_STEPS. Calls swap-x-for-y-simple-range-multi.
119-125 swap-y-for-x-simple-multi Wrapper using MAX_STEPS. Calls swap-y-for-x-simple-range-multi.
128-142 swap-x-for-y-simple-range-multi max-steps within 1..319; total Y output at least min-dy. Repeatedly reads active bin and invokes fixed-core X-for-Y until input is exhausted or step limit is reached.
145-159 swap-y-for-x-simple-range-multi max-steps within 1..319; total X output at least min-dx. Repeatedly reads active bin and invokes fixed-core Y-for-X until input is exhausted or step limit is reached.

Private helpers

Lines Helper Purpose
161-188 fold-swap-multi Executes one independent explicit hop and accumulates unfavorable-bin distance.
190-226 fold-swap-x-for-y-same-multi Executes one same-pair X-for-Y hop with remaining input.
228-264 fold-swap-y-for-x-same-multi Executes one same-pair Y-for-X hop with remaining input.
266-287 fold-swap-simple-multi Executes one simple range-based pool leg.
289-317 fold-swap-x-for-y-simple-multi Executes one active-bin X-for-Y step.
319-347 fold-swap-y-for-x-simple-multi Executes one active-bin Y-for-X step.
349-352 abs-int Converts a signed bin delta to an unsigned absolute value.

3. Post-Condition Coverage Matrix

Callers should attach transaction post-conditions based on the selected route, not rely solely on return values. The core transfers input from tx-sender and output back to tx-sender.

Public function Token movement Recommended caller post-conditions
swap-multi Each hop can send one selected input token from caller to a pool and receive one selected output token from a pool. Token pairs and directions can differ per hop. For every input asset, cap total sent by caller. For every expected output asset, require the route-specific minimum received by caller. Review all hop token traits and pool traits before signing.
swap-x-for-y-same-multi Sends at most amount X from caller across listed pools and receives Y back to caller. Cap caller X sent at amount; require caller Y received at least min-y-amount-total; review pool order and each per-hop floor.
swap-y-for-x-same-multi Sends at most amount Y from caller across listed pools and receives X back to caller. Cap caller Y sent at amount; require caller X received at least min-x-amount-total; review pool order and each per-hop floor.
swap-simple-multi Each of up to five independent legs spends that leg's amount and receives that leg's output. Cap total sent for every input asset after summing same-asset legs; require minimum received per expected output asset; review each leg's min-received and max-steps.
swap-x-for-y-simple-multi Sends at most x-amount X and receives Y. Cap caller X sent at x-amount; require caller Y received at least min-dy.
swap-y-for-x-simple-multi Sends at most y-amount Y and receives X. Cap caller Y sent at y-amount; require caller X received at least min-dx.
swap-x-for-y-simple-range-multi Sends at most x-amount X and receives Y over at most max-steps active bins. Cap caller X sent at x-amount; require caller Y received at least min-dy.
swap-y-for-x-simple-range-multi Sends at most y-amount Y and receives X over at most max-steps active bins. Cap caller Y sent at y-amount; require caller X received at least min-dx.

4. Authority and Access-Control Matrix

Surface Authority Notes
All eight router public functions Open No router-specific authorization check is needed for swaps.
Router state mutation None The router stores no state.
Router owner operations None No owner, admin, pause, kill switch, or configuration setter exists in this router.
Core selection Fixed by router Every executed hop calls deployed fixed core SP1PFR4...dlmm-core-v-1-1.
Pool selection Caller-supplied trait Fixed core validates the pool against its registry and verifies the pool is managed by the fixed core.
Token selection Caller-supplied traits Fixed core checks trait contract principals against the selected pool's X and Y token principals before transfer.
Expected bin IDs Caller-supplied Explicit multi-hop routes validate caller expectations and cap cumulative unfavorable movement.
Simple traversal step count Caller-supplied within bounds Range functions enforce 1..319; wrappers select 319.
Oracle dependency None in router Router pricing is delegated to the managed pool and fixed core bin model.

5. Clarity Best-Practice Review

tx-sender versus contract-caller

The router does not reference either principal directly. The fixed core uses tx-sender for swap token movement, which preserves the original transaction origin while the router composes calls. This matches the intended router behavior.

Panic operations

The router contains no unwrap-panic or unwrap-err-panic. It uses response propagation with try!, typed fallbacks with unwrap!, and bounds checks with asserts!.

Arithmetic

The router's arithmetic is limited to:

  • subtracting active and expected bin IDs after bounding expected IDs;
  • accumulating non-negative unfavorable-bin distance;
  • subtracting downstream-reported consumed input from the caller-specified remaining input; and
  • adding downstream-reported output.

The fixed core returns typed unsigned amounts and Clarity arithmetic traps on underflow or overflow. The maximum explicit unfavorable-bin accumulation is bounded by route length and the -500..500 expected-bin range. No silent wraparound was identified.

as-contract and escalation

The router contains no as-contract. It cannot directly elevate a principal or move tokens. Token movement is delegated to the fixed core and managed pools.

Trait conformance

The router accepts typed pool and SIP-010 trait references. The fixed core checks that supplied token trait principals equal the selected pool's configured token principals and checks pool validity before transfer. No router-level trait gap was identified.

6. Findings Table

ID Severity Function Line Finding Recommended fix
BF-RTR-01 Low swap-multi 48-57 The fully generic route has per-hop min-received checks and a cumulative unfavorable-bin cap, but it has no aggregate output floor. This is flexible for mixed-token routes, but a caller building a same-output route can accidentally protect each hop while omitting an end-to-end receive constraint. The transaction remains atomic and callers can attach post-conditions, so this is a caller-safety gap rather than a direct asset-loss exploit. Document mandatory output-asset post-conditions for generic routes. Consider an additional same-output helper or optional aggregate receive check where route semantics permit it.
BF-RTR-02 Informational swap-multi, same-pair helpers, swap-simple-multi 53-56, 68-74, 86-92, 102-105 Empty-list rejection occurs after the corresponding fold binding is evaluated. Empty folds make no downstream call and the transaction still rejects safely, but validating the path first would make control flow easier to audit and avoid fail-late structure. Move the non-empty assertion before evaluating the fold, for example with an outer begin or wrapper helper.
BF-RTR-03 Informational Range traversal helpers 289-303, 319-333 The folded bin-id iterator value is intentionally unused. STEP_INDEX_RANGE acts as a bounded repetition counter while each step re-reads the pool's active bin. This behavior is valid but non-obvious during review. Rename the folded value to an explicit ignored step index if supported by local style, or add a short comment that traversal deliberately follows the live active bin.
BF-RTR-04 Informational swap-x-for-y-simple-range-multi 127 The comment says the function performs Y-for-X while the implementation and function name perform X-for-Y. Correct the comment to X-for-Y.

Conclusion

The deployed router is a small stateless composition layer over a fixed DLMM core. Its principal protections are downstream managed-pool validation, downstream token-principal validation, per-call minimum receive checks, bounded range traversal, and atomic rollback. The review found no high- or critical-severity issue. The main improvement is clearer end-to-end caller protection guidance for the generic multi-route entry point.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment