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-1SM1FKXGNZJWSTWDWXQZJNF7B5TV5ZB235JTCXYXKD.sip-010-trait-ft-standard-v-1-1SP1PFR4V08H1RAZXREBGFFQ59WB739XM8VVGTFSEA.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.
The router is stateless. It defines no data-var and no data-map.
| 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. |
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-senderto 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-feesmap.
All router assertions and downstream try! failures revert the transaction
atomically.
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. |
| 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. |
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. |
| 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. |
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.
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!.
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.
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.
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.
| 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. |
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.