Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 12 additions & 4 deletions clarity/contracts/dlmm-swap-router-v-1-1.clar
Original file line number Diff line number Diff line change
Expand Up @@ -96,12 +96,17 @@
;; Swap through up to 319 bins in up to 5 pools
(define-public (swap-simple-multi
(swaps (list 5 {pool-trait: <dlmm-pool-trait>, x-token-trait: <sip-010-trait>, y-token-trait: <sip-010-trait>, amount: uint, min-received: uint, x-for-y: bool, max-steps: uint}))
(min-final-output uint)
)
(let (
(swap-result (try! (fold fold-swap-simple-multi swaps (ok {results: (list )}))))
(swap-result (try! (fold fold-swap-simple-multi swaps (ok {results: (list ), final-output: u0}))))
)
(asserts! (> (len swaps) u0) ERR_EMPTY_SWAPS_LIST)
(ok swap-result)
(asserts! (>= (get final-output swap-result) min-final-output) ERR_MINIMUM_RECEIVED)
(ok {
results: (get results swap-result),
final-output: (get final-output swap-result)
})
)
)

Expand Down Expand Up @@ -264,7 +269,7 @@

(define-private (fold-swap-simple-multi
(swap {pool-trait: <dlmm-pool-trait>, x-token-trait: <sip-010-trait>, y-token-trait: <sip-010-trait>, amount: uint, min-received: uint, x-for-y: bool, max-steps: uint})
(result (response {results: (list 5 {in: uint, out: uint})} uint))
(result (response {results: (list 5 {in: uint, out: uint}), final-output: uint} uint))
)
(let (
(result-data (unwrap! result ERR_NO_RESULT_DATA))
Expand All @@ -281,7 +286,10 @@
(try! (swap-y-for-x-simple-range-multi pool-trait x-token-trait y-token-trait amount min-received max-steps))))
(updated-results (unwrap! (as-max-len? (append (get results result-data) swap-result) u5) ERR_RESULTS_LIST_OVERFLOW))
)
(ok {results: updated-results})
(ok {
results: updated-results,
final-output: (get out swap-result)
})
)
)

Expand Down
16 changes: 15 additions & 1 deletion clarity/tests/clarigen-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2746,6 +2746,21 @@ dlmmSwapRouterV11: {
"out": bigint;
}[];
"unfavorable": bigint;
}, bigint>>,
swapSimpleMulti: {"name":"swap-simple-multi","access":"public","args":[{"name":"swaps","type":{"list":{"type":{"tuple":[{"name":"amount","type":"uint128"},{"name":"max-steps","type":"uint128"},{"name":"min-received","type":"uint128"},{"name":"pool-trait","type":"trait_reference"},{"name":"x-for-y","type":"bool"},{"name":"x-token-trait","type":"trait_reference"},{"name":"y-token-trait","type":"trait_reference"}]},"length":5}}},{"name":"min-final-output","type":"uint128"}],"outputs":{"type":{"response":{"ok":{"tuple":[{"name":"final-output","type":"uint128"},{"name":"results","type":{"list":{"type":{"tuple":[{"name":"in","type":"uint128"},{"name":"out","type":"uint128"}]},"length":5}}}]},"error":"uint128"}}}} as TypedAbiFunction<[swaps: TypedAbiArg<{
"amount": number | bigint;
"maxSteps": number | bigint;
"minReceived": number | bigint;
"poolTrait": string;
"xForY": boolean;
"xTokenTrait": string;
"yTokenTrait": string;
}[], "swaps">, minFinalOutput: TypedAbiArg<number | bigint, "minFinalOutput">], Response<{
"finalOutput": bigint;
"results": {
"in": bigint;
"out": bigint;
}[];
}, bigint>>,
swapXForYSameMulti: {"name":"swap-x-for-y-same-multi","access":"public","args":[{"name":"swaps","type":{"list":{"type":{"tuple":[{"name":"expected-bin-id","type":"int128"},{"name":"min-received","type":"uint128"},{"name":"pool-trait","type":"trait_reference"}]},"length":350}}},{"name":"x-token-trait","type":"trait_reference"},{"name":"y-token-trait","type":"trait_reference"},{"name":"amount","type":"uint128"},{"name":"min-y-amount-total","type":"uint128"},{"name":"max-unfavorable-bins","type":"uint128"}],"outputs":{"type":{"response":{"ok":{"tuple":[{"name":"results","type":{"list":{"type":{"tuple":[{"name":"in","type":"uint128"},{"name":"out","type":"uint128"}]},"length":350}}},{"name":"unfavorable","type":"uint128"},{"name":"y-amount","type":"uint128"}]},"error":"uint128"}}}} as TypedAbiFunction<[swaps: TypedAbiArg<{
"expectedBinId": number | bigint;
Expand Down Expand Up @@ -4166,4 +4181,3 @@ export const project = {
contracts,
deployments,
} as const;

16 changes: 15 additions & 1 deletion clarity/tests/helpers/clarigen-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2746,6 +2746,21 @@ dlmmSwapRouterV11: {
"out": bigint;
}[];
"unfavorable": bigint;
}, bigint>>,
swapSimpleMulti: {"name":"swap-simple-multi","access":"public","args":[{"name":"swaps","type":{"list":{"type":{"tuple":[{"name":"amount","type":"uint128"},{"name":"max-steps","type":"uint128"},{"name":"min-received","type":"uint128"},{"name":"pool-trait","type":"trait_reference"},{"name":"x-for-y","type":"bool"},{"name":"x-token-trait","type":"trait_reference"},{"name":"y-token-trait","type":"trait_reference"}]},"length":5}}},{"name":"min-final-output","type":"uint128"}],"outputs":{"type":{"response":{"ok":{"tuple":[{"name":"final-output","type":"uint128"},{"name":"results","type":{"list":{"type":{"tuple":[{"name":"in","type":"uint128"},{"name":"out","type":"uint128"}]},"length":5}}}]},"error":"uint128"}}}} as TypedAbiFunction<[swaps: TypedAbiArg<{
"amount": number | bigint;
"maxSteps": number | bigint;
"minReceived": number | bigint;
"poolTrait": string;
"xForY": boolean;
"xTokenTrait": string;
"yTokenTrait": string;
}[], "swaps">, minFinalOutput: TypedAbiArg<number | bigint, "minFinalOutput">], Response<{
"finalOutput": bigint;
"results": {
"in": bigint;
"out": bigint;
}[];
}, bigint>>,
swapXForYSameMulti: {"name":"swap-x-for-y-same-multi","access":"public","args":[{"name":"swaps","type":{"list":{"type":{"tuple":[{"name":"expected-bin-id","type":"int128"},{"name":"min-received","type":"uint128"},{"name":"pool-trait","type":"trait_reference"}]},"length":350}}},{"name":"x-token-trait","type":"trait_reference"},{"name":"y-token-trait","type":"trait_reference"},{"name":"amount","type":"uint128"},{"name":"min-y-amount-total","type":"uint128"},{"name":"max-unfavorable-bins","type":"uint128"}],"outputs":{"type":{"response":{"ok":{"tuple":[{"name":"results","type":{"list":{"type":{"tuple":[{"name":"in","type":"uint128"},{"name":"out","type":"uint128"}]},"length":350}}},{"name":"unfavorable","type":"uint128"},{"name":"y-amount","type":"uint128"}]},"error":"uint128"}}}} as TypedAbiFunction<[swaps: TypedAbiArg<{
"expectedBinId": number | bigint;
Expand Down Expand Up @@ -4166,4 +4181,3 @@ export const project = {
contracts,
deployments,
} as const;

66 changes: 65 additions & 1 deletion clarity/tests/routers/swap-router.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -494,6 +494,70 @@ describe('DLMM Swap Helper Functions', () => {
expect(received).toBeGreaterThanOrEqual(0n);
});

it('should enforce a final output minimum for simple multi-hop swaps', async () => {
const swaps = [
{
poolTrait: sbtcUsdcPool.identifier,
xTokenTrait: mockSbtcToken.identifier,
yTokenTrait: mockUsdcToken.identifier,
amount: 500000n,
minReceived: 1n,
xForY: true,
maxSteps: 10n
},
{
poolTrait: sbtcUsdcPool.identifier,
xTokenTrait: mockSbtcToken.identifier,
yTokenTrait: mockUsdcToken.identifier,
amount: 25000000n,
minReceived: 1n,
xForY: false,
maxSteps: 10n
}
];

const response = txOk(dlmmSwapRouter.swapSimpleMulti(
swaps,
1n
), alice);

const result = cvToValue(response.result);
const lastResult = result.results[result.results.length - 1];

expect(result.finalOutput).toBe(lastResult.out);
expect(result.finalOutput).toBeGreaterThan(0n);
});

it('should fail simple multi-hop swaps when final output is below minimum', async () => {
const swaps = [
{
poolTrait: sbtcUsdcPool.identifier,
xTokenTrait: mockSbtcToken.identifier,
yTokenTrait: mockUsdcToken.identifier,
amount: 500000n,
minReceived: 1n,
xForY: true,
maxSteps: 10n
},
{
poolTrait: sbtcUsdcPool.identifier,
xTokenTrait: mockSbtcToken.identifier,
yTokenTrait: mockUsdcToken.identifier,
amount: 25000000n,
minReceived: 1n,
xForY: false,
maxSteps: 10n
}
];

const response = txErr(dlmmSwapRouter.swapSimpleMulti(
swaps,
999999999999n
), alice);

expect(cvToValue(response.result)).toBe(errors.dlmmSwapRouter.ERR_MINIMUM_RECEIVED);
});

it('should fail when using random token in swap helper', async () => {
// Mint random tokens for testing
txOk(mockRandomToken.mint(1000000n, alice), deployer);
Expand Down Expand Up @@ -544,4 +608,4 @@ describe('DLMM Swap Helper Functions', () => {
expect(cvToValue(response.result)).toBe(errors.dlmmCore.ERR_INVALID_Y_TOKEN);
});
});
});
});
40 changes: 39 additions & 1 deletion docs/dlmm-swap-router-v-1-1.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ dlmm-swap-router-v-1-1
- [`swap-multi`](#swap-multi)
- [`swap-x-for-y-same-multi`](#swap-x-for-y-same-multi)
- [`swap-y-for-x-same-multi`](#swap-y-for-x-same-multi)
- [`swap-simple-multi`](#swap-simple-multi)
- [`swap-x-for-y-simple-multi`](#swap-x-for-y-simple-multi)
- [`swap-y-for-x-simple-multi`](#swap-y-for-x-simple-multi)

Expand Down Expand Up @@ -173,6 +174,44 @@ Swap through multiple bins in multiple pools using the same token pair and Y for
| min-x-amount-total | uint |
| max-unfavorable-bins | uint |

### swap-simple-multi

[View in file](../clarity/contracts/dlmm-swap-router-v-1-1.clar#L97)

`(define-public (swap-simple-multi ((swaps (list 5 (tuple (amount uint) (max-steps uint) (min-received uint) (pool-trait trait_reference) (x-for-y bool) (x-token-trait trait_reference) (y-token-trait trait_reference)))) (min-final-output uint)) (response (tuple (final-output uint) (results (list 5 (tuple (in uint) (out uint))))) uint))`

Swap through up to 319 bins in up to 5 pools and require the final leg output to meet `min-final-output`.

<details>
<summary>Source code:</summary>

```clarity
(define-public (swap-simple-multi
(swaps (list 5 {pool-trait: <dlmm-pool-trait>, x-token-trait: <sip-010-trait>, y-token-trait: <sip-010-trait>, amount: uint, min-received: uint, x-for-y: bool, max-steps: uint}))
(min-final-output uint)
)
(let (
(swap-result (try! (fold fold-swap-simple-multi swaps (ok {results: (list ), final-output: u0}))))
)
(asserts! (> (len swaps) u0) ERR_EMPTY_SWAPS_LIST)
(asserts! (>= (get final-output swap-result) min-final-output) ERR_MINIMUM_RECEIVED)
(ok {
results: (get results swap-result),
final-output: (get final-output swap-result)
})
)
)
```
</details>


**Parameters:**

| Name | Type |
| --- | --- |
| swaps | (list 5 (tuple (amount uint) (max-steps uint) (min-received uint) (pool-trait trait_reference) (x-for-y bool) (x-token-trait trait_reference) (y-token-trait trait_reference))) |
| min-final-output | uint |

### swap-x-for-y-simple-multi

[View in file](../clarity/contracts/dlmm-swap-router-v-1-1.clar#L93)
Expand Down Expand Up @@ -722,4 +761,3 @@ List used to swap through up to 350 bins via swap-x-for-y-simple-multi and swap-
```

[View in file](../clarity/contracts/dlmm-swap-router-v-1-1.clar#L23)