Multi-hop swap router for the Velar DEX โ supports 2-to-5 token paths via Uniswap V2-style AMM pools
| Contract | SP1Y5YSTAHZ88XYK1VPDH24GY0HPX5J4JECTMY4A1.univ2-path2 |
| Protocol | Velar โ Uniswap V2-style DEX on Stacks |
| Source | Verified on-chain via Hiro API (/v2/contracts/source) |
| Clarity Version | Pre-Clarity 4 (publish height 153315) |
| Lines of Code | ~150 |
| Confidence | ๐ข HIGH โ simple routing contract, all logic visible; delegates actual swaps to univ2-router |
| Date | February 25, 2026 |
univ2-path2 is a multi-hop swap routing contract for the Velar DEX. It composes single-hop swaps through univ2-router.swap-exact-tokens-for-tokens to support paths of 2 to 5 tokens. The contract includes its own copy of the constant-product AMM formula (get-amount-out) for computing expected outputs and providing read-only quote functions.
do-swap looks up the pool via univ2-core.lookup-pool, computes the expected output using the local AMM formula, and delegates execution to univ2-router.swap-3, swap-4, swap-5 chain sequential do-swap calls, piping each hop's output as the next hop's input. A final amt-out-min check protects the entire path.get-amount-out-3/4/5 provide read-only multi-hop output estimates using the same formula.univ2-core correctly reports pool reserves and swap feesuniv2-router executes swaps consistently with the AMM formula replicated in this contractshare-fee-to trait implementation is trusted (user-supplied)Location: swap-args โ get-amount-out
Description: The get-amount-out function uses integer division, which truncates to zero for very small inputs relative to reserves. When this happens, swap-args sets amt-out-min: u0, and the swap proceeds โ the user spends their input tokens and receives nothing in return.
;; get-amount-out can return 0 for dust amounts:
;; (/ (* amt-in-adjusted reserve-out)
;; (+ reserve-in amt-in-adjusted))
;; โ 0 when amt-in-adjusted * reserve-out < reserve-in + amt-in-adjusted
;; swap-args only validates input, not output:
(asserts! (and (> amt-in u0)) err-preconditions)
;; Missing: (asserts! (> amt-out u0) err-postconditions)
Impact: Users sending very small amounts (dust) will lose their tokens with zero output. While unlikely for intentional swaps, this can occur in multi-hop paths where intermediate outputs shrink through compounding rounding losses. Bots or contracts composing with this router could trigger this inadvertently.
Recommendation: Add output validation in swap-args:
(asserts! (> amt-out u0) err-postconditions)
unwrap-panic in swap-args produces unhelpful errorsLocation: swap-args function โ two unwrap-panic calls
Description: swap-args uses unwrap-panic for both lookup-pool and get-pool-id results. If a pool doesn't exist for the given token pair, the transaction aborts with a runtime panic rather than returning a descriptive error code.
(let ((res (unwrap-panic
(contract-call? .univ2-core lookup-pool
(contract-of token-in) (contract-of token-out))))
(id (unwrap-panic
(contract-call? .univ2-core get-pool-id ...))))
Impact: Users and integrators see an opaque runtime error instead of a meaningful error code when attempting to swap through a non-existent pool. This complicates debugging and error handling for downstream contracts.
Recommendation: Replace unwrap-panic with unwrap! and a descriptive error constant (e.g., (err u2003) for "pool not found").
ids parameter in get-amount-out-4Location: get-amount-out-4
Description: The function accepts an (ids (list 4 uint)) parameter that is never referenced in the function body. The other quote functions (get-amount-out-3, get-amount-out-5) do not have this parameter, suggesting it was left in by mistake.
(define-read-only
(get-amount-out-4
(amt-in uint)
(token-a <ft-trait>)
(token-b <ft-trait>)
(token-c <ft-trait>)
(token-d <ft-trait>)
(ids (list 4 uint))) ;; <-- never used
...)
Impact: Callers must supply a meaningless parameter. Since the contract is deployed and immutable, this cannot be changed, but integrators should be aware they can pass any list value.
Recommendation: If redeploying, remove the unused parameter. Document for integrators that ids is ignored.
Description: Deployed before Clarity 4. This contract doesn't directly hold or transfer assets (it delegates to univ2-router), so the lack of as-contract? is not a direct risk here. However, it cannot use contract-hash? for on-chain verification.
Recommendation: If the router is ever redeployed, use Clarity 4.
Description: Unlike Uniswap V2's router (which accepts a deadline parameter), this contract has no mechanism to expire stale transactions. A signed transaction could sit in the mempool and execute much later when market conditions have changed unfavorably.
Impact: Users relying on the amt-out-min parameter are still protected against slippage, but may receive a worse-than-expected price if market conditions shift between signing and execution.
Recommendation: Add an optional block-height deadline parameter: (asserts! (<= block-height deadline) (err u2004))
Description: do-swap computes the expected output via get-amount-out and passes it as amt-out-min to the router โ zero slippage tolerance per hop. This works if the router's internal formula is identical. Any discrepancy (rounding, fee calculation differences) would cause every swap to fail.
Impact: If univ2-router uses even a slightly different formula or rounding strategy than the local get-amount-out, all swaps through this contract would revert. In practice, the formula appears to be consistent (the router likely uses the same constant-product formula), but this is a fragile coupling.
Recommendation: This is by design and works as long as the formulas match. Document the dependency for future maintainers.
swap-3/4/5) enforce a user-specified amt-out-min on the final output, protecting against sandwich attacks across the full path.flipped flag from lookup-pool is properly handled โ reserves and token ordering are swapped consistently.(get amt-out b), maintaining type safety.get-amount-out-3/4/5 allow UIs and bots to preview multi-hop outputs without executing transactions.SP1Y5YSTAHZ88XYK1VPDH24GY0HPX5J4JECTMY4A1.univ2-router โ executes actual swapsSP1Y5YSTAHZ88XYK1VPDH24GY0HPX5J4JECTMY4A1.univ2-core โ pool registry and reserve storage