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
8 changes: 5 additions & 3 deletions clarity/contracts/dlmm-swap-router-v-1-1.clar
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,13 @@
(let (
(swap-result (try! (fold fold-swap-x-for-y-same-multi swaps (ok {x-token-trait: x-token-trait, y-token-trait: y-token-trait, results: (list ), x-amount-for-swap: amount, y-amount: u0, unfavorable: u0}))))
(y-amount-total (get y-amount swap-result))
(x-amount-spent (- amount (get x-amount-for-swap swap-result)))
(unfavorable (get unfavorable swap-result))
)
(asserts! (> (len swaps) u0) ERR_EMPTY_SWAPS_LIST)
(asserts! (<= unfavorable max-unfavorable-bins) ERR_BIN_SLIPPAGE)
(asserts! (>= y-amount-total min-y-amount-total) ERR_MINIMUM_Y_AMOUNT)
(ok {results: (get results swap-result), y-amount: y-amount-total, unfavorable: unfavorable})
(ok {results: (get results swap-result), x-amount-spent: x-amount-spent, y-amount: y-amount-total, unfavorable: unfavorable})
)
)

Expand All @@ -80,12 +81,13 @@
(let (
(swap-result (try! (fold fold-swap-y-for-x-same-multi swaps (ok {x-token-trait: x-token-trait, y-token-trait: y-token-trait, results: (list ), y-amount-for-swap: amount, x-amount: u0, unfavorable: u0}))))
(x-amount-total (get x-amount swap-result))
(y-amount-spent (- amount (get y-amount-for-swap swap-result)))
(unfavorable (get unfavorable swap-result))
)
(asserts! (> (len swaps) u0) ERR_EMPTY_SWAPS_LIST)
(asserts! (<= unfavorable max-unfavorable-bins) ERR_BIN_SLIPPAGE)
(asserts! (>= x-amount-total min-x-amount-total) ERR_MINIMUM_X_AMOUNT)
(ok {results: (get results swap-result), x-amount: x-amount-total, unfavorable: unfavorable})
(ok {results: (get results swap-result), y-amount-spent: y-amount-spent, x-amount: x-amount-total, unfavorable: unfavorable})
)
)

Expand Down Expand Up @@ -289,4 +291,4 @@
;; Get absolute value of a signed int as uint
(define-private (abs-int (value int))
(to-uint (if (>= value 0) value (- value)))
)
)
7 changes: 4 additions & 3 deletions clarity/tests/clarigen-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2747,7 +2747,7 @@ dlmmSwapRouterV11: {
}[];
"unfavorable": 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<{
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":"x-amount-spent","type":"uint128"},{"name":"y-amount","type":"uint128"}]},"error":"uint128"}}}} as TypedAbiFunction<[swaps: TypedAbiArg<{
"expectedBinId": number | bigint;
"minReceived": number | bigint;
"poolTrait": string;
Expand All @@ -2757,13 +2757,14 @@ dlmmSwapRouterV11: {
"out": bigint;
}[];
"unfavorable": bigint;
"xAmountSpent": bigint;
"yAmount": bigint;
}, bigint>>,
swapXForYSimpleMulti: {"name":"swap-x-for-y-simple-multi","access":"public","args":[{"name":"pool-trait","type":"trait_reference"},{"name":"x-token-trait","type":"trait_reference"},{"name":"y-token-trait","type":"trait_reference"},{"name":"x-amount","type":"uint128"},{"name":"min-dy","type":"uint128"}],"outputs":{"type":{"response":{"ok":{"tuple":[{"name":"in","type":"uint128"},{"name":"out","type":"uint128"}]},"error":"uint128"}}}} as TypedAbiFunction<[poolTrait: TypedAbiArg<string, "poolTrait">, xTokenTrait: TypedAbiArg<string, "xTokenTrait">, yTokenTrait: TypedAbiArg<string, "yTokenTrait">, xAmount: TypedAbiArg<number | bigint, "xAmount">, minDy: TypedAbiArg<number | bigint, "minDy">], Response<{
"in": bigint;
"out": bigint;
}, bigint>>,
swapYForXSameMulti: {"name":"swap-y-for-x-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-x-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":"x-amount","type":"uint128"}]},"error":"uint128"}}}} as TypedAbiFunction<[swaps: TypedAbiArg<{
swapYForXSameMulti: {"name":"swap-y-for-x-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-x-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":"x-amount","type":"uint128"},{"name":"y-amount-spent","type":"uint128"}]},"error":"uint128"}}}} as TypedAbiFunction<[swaps: TypedAbiArg<{
"expectedBinId": number | bigint;
"minReceived": number | bigint;
"poolTrait": string;
Expand All @@ -2774,6 +2775,7 @@ dlmmSwapRouterV11: {
}[];
"unfavorable": bigint;
"xAmount": bigint;
"yAmountSpent": bigint;
}, bigint>>,
swapYForXSimpleMulti: {"name":"swap-y-for-x-simple-multi","access":"public","args":[{"name":"pool-trait","type":"trait_reference"},{"name":"x-token-trait","type":"trait_reference"},{"name":"y-token-trait","type":"trait_reference"},{"name":"y-amount","type":"uint128"},{"name":"min-dx","type":"uint128"}],"outputs":{"type":{"response":{"ok":{"tuple":[{"name":"in","type":"uint128"},{"name":"out","type":"uint128"}]},"error":"uint128"}}}} as TypedAbiFunction<[poolTrait: TypedAbiArg<string, "poolTrait">, xTokenTrait: TypedAbiArg<string, "xTokenTrait">, yTokenTrait: TypedAbiArg<string, "yTokenTrait">, yAmount: TypedAbiArg<number | bigint, "yAmount">, minDx: TypedAbiArg<number | bigint, "minDx">], Response<{
"in": bigint;
Expand Down Expand Up @@ -4166,4 +4168,3 @@ export const project = {
contracts,
deployments,
} as const;

7 changes: 4 additions & 3 deletions clarity/tests/helpers/clarigen-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2747,7 +2747,7 @@ dlmmSwapRouterV11: {
}[];
"unfavorable": 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<{
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":"x-amount-spent","type":"uint128"},{"name":"y-amount","type":"uint128"}]},"error":"uint128"}}}} as TypedAbiFunction<[swaps: TypedAbiArg<{
"expectedBinId": number | bigint;
"minReceived": number | bigint;
"poolTrait": string;
Expand All @@ -2757,13 +2757,14 @@ dlmmSwapRouterV11: {
"out": bigint;
}[];
"unfavorable": bigint;
"xAmountSpent": bigint;
"yAmount": bigint;
}, bigint>>,
swapXForYSimpleMulti: {"name":"swap-x-for-y-simple-multi","access":"public","args":[{"name":"pool-trait","type":"trait_reference"},{"name":"x-token-trait","type":"trait_reference"},{"name":"y-token-trait","type":"trait_reference"},{"name":"x-amount","type":"uint128"},{"name":"min-dy","type":"uint128"}],"outputs":{"type":{"response":{"ok":{"tuple":[{"name":"in","type":"uint128"},{"name":"out","type":"uint128"}]},"error":"uint128"}}}} as TypedAbiFunction<[poolTrait: TypedAbiArg<string, "poolTrait">, xTokenTrait: TypedAbiArg<string, "xTokenTrait">, yTokenTrait: TypedAbiArg<string, "yTokenTrait">, xAmount: TypedAbiArg<number | bigint, "xAmount">, minDy: TypedAbiArg<number | bigint, "minDy">], Response<{
"in": bigint;
"out": bigint;
}, bigint>>,
swapYForXSameMulti: {"name":"swap-y-for-x-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-x-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":"x-amount","type":"uint128"}]},"error":"uint128"}}}} as TypedAbiFunction<[swaps: TypedAbiArg<{
swapYForXSameMulti: {"name":"swap-y-for-x-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-x-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":"x-amount","type":"uint128"},{"name":"y-amount-spent","type":"uint128"}]},"error":"uint128"}}}} as TypedAbiFunction<[swaps: TypedAbiArg<{
"expectedBinId": number | bigint;
"minReceived": number | bigint;
"poolTrait": string;
Expand All @@ -2774,6 +2775,7 @@ dlmmSwapRouterV11: {
}[];
"unfavorable": bigint;
"xAmount": bigint;
"yAmountSpent": bigint;
}, bigint>>,
swapYForXSimpleMulti: {"name":"swap-y-for-x-simple-multi","access":"public","args":[{"name":"pool-trait","type":"trait_reference"},{"name":"x-token-trait","type":"trait_reference"},{"name":"y-token-trait","type":"trait_reference"},{"name":"y-amount","type":"uint128"},{"name":"min-dx","type":"uint128"}],"outputs":{"type":{"response":{"ok":{"tuple":[{"name":"in","type":"uint128"},{"name":"out","type":"uint128"}]},"error":"uint128"}}}} as TypedAbiFunction<[poolTrait: TypedAbiArg<string, "poolTrait">, xTokenTrait: TypedAbiArg<string, "xTokenTrait">, yTokenTrait: TypedAbiArg<string, "yTokenTrait">, yAmount: TypedAbiArg<number | bigint, "yAmount">, minDx: TypedAbiArg<number | bigint, "minDx">], Response<{
"in": bigint;
Expand Down Expand Up @@ -4166,4 +4168,3 @@ export const project = {
contracts,
deployments,
} as const;

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

it('should report aggregate spent amount for X-for-Y same-token multi swaps', async () => {
const swaps = [{
poolTrait: sbtcUsdcPool.identifier,
expectedBinId: 0,
minReceived: 1n
}];
const amount = 1000000n; // 0.01 BTC
const maxUnfavorableBins = 5n;

const initialXBalance = rovOk(mockSbtcToken.getBalance(alice));
const initialYBalance = rovOk(mockUsdcToken.getBalance(alice));

const response = txOk(dlmmSwapRouter.swapXForYSameMulti(
swaps,
mockSbtcToken.identifier,
mockUsdcToken.identifier,
amount,
1n,
maxUnfavorableBins
), alice);

const finalXBalance = rovOk(mockSbtcToken.getBalance(alice));
const finalYBalance = rovOk(mockUsdcToken.getBalance(alice));
const result = cvToValue(response.result);
const spent = result.results.reduce((sum: bigint, r: {in: bigint, out: bigint}) => sum + r.in, 0n);
const received = result.results.reduce((sum: bigint, r: {in: bigint, out: bigint}) => sum + r.out, 0n);

expect(result.xAmountSpent).toBe(spent);
expect(result.yAmount).toBe(received);
expect(finalXBalance).toBe(initialXBalance - result.xAmountSpent);
expect(finalYBalance).toBe(initialYBalance + result.yAmount);
expect(result.xAmountSpent).toBeGreaterThan(0n);
});

it('should report aggregate spent amount for Y-for-X same-token multi swaps', async () => {
const swaps = [{
poolTrait: sbtcUsdcPool.identifier,
expectedBinId: 0,
minReceived: 1n
}];
const amount = 50000000n; // 50 USDC
const maxUnfavorableBins = 5n;

const initialXBalance = rovOk(mockSbtcToken.getBalance(alice));
const initialYBalance = rovOk(mockUsdcToken.getBalance(alice));

const response = txOk(dlmmSwapRouter.swapYForXSameMulti(
swaps,
mockSbtcToken.identifier,
mockUsdcToken.identifier,
amount,
1n,
maxUnfavorableBins
), alice);

const finalXBalance = rovOk(mockSbtcToken.getBalance(alice));
const finalYBalance = rovOk(mockUsdcToken.getBalance(alice));
const result = cvToValue(response.result);
const spent = result.results.reduce((sum: bigint, r: {in: bigint, out: bigint}) => sum + r.in, 0n);
const received = result.results.reduce((sum: bigint, r: {in: bigint, out: bigint}) => sum + r.out, 0n);

expect(result.yAmountSpent).toBe(spent);
expect(result.xAmount).toBe(received);
expect(finalYBalance).toBe(initialYBalance - result.yAmountSpent);
expect(finalXBalance).toBe(initialXBalance + result.xAmount);
expect(result.yAmountSpent).toBeGreaterThan(0n);
});

it('should fail when minimum received not met', async () => {
const swaps = [{
poolTrait: sbtcUsdcPool.identifier,
Expand Down Expand Up @@ -544,4 +612,4 @@ describe('DLMM Swap Helper Functions', () => {
expect(cvToValue(response.result)).toBe(errors.dlmmCore.ERR_INVALID_Y_TOKEN);
});
});
});
});