Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: solvers - consume full swap amount #10

Merged
merged 2 commits into from
Oct 4, 2024
Merged
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
18 changes: 14 additions & 4 deletions models/src/replicating/libraries/SwapLib.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,12 @@ export const getSwapAmounts = ({
? Decimal.max(thresholdSqrtPrice, scaledLowerSqrtPrice)
: scaledLowerSqrtPrice;

const { amountIn, amountOut, fees, nextSqrtPrice } = computeSwapAmount({
const {
amountIn: netAmountIn,
amountOut,
fees,
nextSqrtPrice,
} = computeSwapAmount({
currSqrtPrice: startSqrtPrice,
targetSqrtPrice,
liquidity,
Expand All @@ -60,19 +65,24 @@ export const getSwapAmounts = ({
exactInput,
});

const grossAmountIn = new Decimal(amountIn).add(fees);
const amountIn =
nextSqrtPrice !== targetSqrtPrice &&
new Decimal(liquidity).gt(0) &&
exactInput
? amount
: new Decimal(netAmountIn).add(fees);

if (thresholdAmount) {
if (exactInput) {
if (amountOut < thresholdAmount)
throw new Error("Threshold amount not met");
} else {
if (grossAmountIn > thresholdAmount)
if (amountIn > thresholdAmount)
throw new Error("Threshold amount exceeded");
}
}

return { amountIn: grossAmountIn, amountOut, fees };
return { amountIn, amountOut, fees };
};

export const computeSwapAmount = ({
Expand Down
120 changes: 120 additions & 0 deletions models/src/replicating/tests/SwapLib.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import Decimal from "decimal.js";
import { getSwapAmounts } from "../libraries/SwapLib";

type SwapParams = {
isBuy: boolean;
exactInput: boolean;
amount: Decimal.Value;
swapFeeRate: Decimal.Value;
thresholdSqrtPrice: Decimal.Value | null;
thresholdAmount: Decimal.Value | null;
lowerSqrtPrice: Decimal.Value;
upperSqrtPrice: Decimal.Value;
liquidity: Decimal.Value;
baseDecimals: number;
quoteDecimals: number;
};
const testGetSwapAmounts = () => {
const cases: SwapParams[] = [
{
isBuy: true,
exactInput: true,
amount: 1,
swapFeeRate: 0,
thresholdSqrtPrice: null,
thresholdAmount: null,
lowerSqrtPrice: 0.8 ** 0.5,
upperSqrtPrice: 1,
liquidity: 10000,
baseDecimals: 18,
quoteDecimals: 18,
},
{
isBuy: true,
exactInput: true,
amount: 1,
swapFeeRate: 0.005,
thresholdSqrtPrice: null,
thresholdAmount: null,
lowerSqrtPrice: 1,
upperSqrtPrice: 1.2 ** 0.5,
liquidity: 0,
baseDecimals: 18,
quoteDecimals: 18,
},
{
isBuy: false,
exactInput: true,
amount: 10,
swapFeeRate: 0.005,
thresholdSqrtPrice: 0.95 ** 0.5,
thresholdAmount: null,
lowerSqrtPrice: 0.8 ** 0.5,
upperSqrtPrice: 1,
liquidity: 200,
baseDecimals: 18,
quoteDecimals: 18,
},
{
isBuy: true,
exactInput: true,
amount: 10,
swapFeeRate: 0.005,
thresholdSqrtPrice: 1.05 ** 0.5,
thresholdAmount: null,
lowerSqrtPrice: 1,
upperSqrtPrice: 1.2 ** 0.5,
liquidity: 200,
baseDecimals: 18,
quoteDecimals: 18,
},
{
isBuy: true,
exactInput: true,
amount: 9.26891,
swapFeeRate: 0.005,
thresholdSqrtPrice: null,
thresholdAmount: null,
lowerSqrtPrice: 0.375 ** 0.5,
upperSqrtPrice: 0.4 ** 0.5,
liquidity: 1,
baseDecimals: 18,
quoteDecimals: 6,
},
];

for (let i = 0; i < cases.length; i++) {
const params = cases[i];
const { amountIn, amountOut, fees } = getSwapAmounts({
isBuy: params.isBuy,
exactInput: params.exactInput,
amount: params.amount,
swapFeeRate: params.swapFeeRate,
thresholdSqrtPrice: params.thresholdSqrtPrice,
thresholdAmount: params.thresholdAmount,
lowerSqrtPrice: params.lowerSqrtPrice,
upperSqrtPrice: params.upperSqrtPrice,
liquidity: params.liquidity,
baseDecimals: params.baseDecimals,
quoteDecimals: params.quoteDecimals,
});
console.log(`Case ${i + 1}`);
const inDecimals = params.isBuy
? params.quoteDecimals
: params.baseDecimals;
const outDecimals = params.isBuy
? params.baseDecimals
: params.quoteDecimals;
console.log({
amountIn: new Decimal(amountIn)
.mul(new Decimal(10).pow(inDecimals))
.toFixed(0),
amountOut: new Decimal(amountOut)
.mul(new Decimal(10).pow(outDecimals))
.toFixed(0),
fees: new Decimal(fees).mul(new Decimal(10).pow(inDecimals)).toFixed(0),
});
}
};

testGetSwapAmounts();
18 changes: 14 additions & 4 deletions models/src/reversion/libraries/SwapLib.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,12 @@ export const getSwapAmounts = ({
? Decimal.max(thresholdSqrtPrice, scaledLowerSqrtPrice)
: scaledLowerSqrtPrice;

const { amountIn, amountOut, fees, nextSqrtPrice } = computeSwapAmount({
const {
amountIn: netAmountIn,
amountOut,
fees,
nextSqrtPrice,
} = computeSwapAmount({
currSqrtPrice: startSqrtPrice,
targetSqrtPrice,
liquidity,
Expand All @@ -60,19 +65,24 @@ export const getSwapAmounts = ({
exactInput,
});

const grossAmountIn = new Decimal(amountIn).add(fees);
const amountIn =
nextSqrtPrice !== targetSqrtPrice &&
new Decimal(liquidity).gt(0) &&
exactInput
? amount
: new Decimal(netAmountIn).add(fees);

if (thresholdAmount) {
if (exactInput) {
if (amountOut < thresholdAmount)
throw new Error("Threshold amount not met");
} else {
if (grossAmountIn > thresholdAmount)
if (amountIn > thresholdAmount)
throw new Error("Threshold amount exceeded");
}
}

return { amountIn: grossAmountIn, amountOut, fees };
return { amountIn, amountOut, fees };
};

export const computeSwapAmount = ({
Expand Down
68 changes: 49 additions & 19 deletions models/src/reversion/tests/SwapLib.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ type SwapParams = {
lowerSqrtPrice: Decimal.Value;
upperSqrtPrice: Decimal.Value;
liquidity: Decimal.Value;
baseDecimals: number;
quoteDecimals: number;
};
const testGetSwapAmounts = () => {
const cases: SwapParams[] = [
Expand All @@ -24,6 +26,8 @@ const testGetSwapAmounts = () => {
lowerSqrtPrice: 0.8 ** 0.5,
upperSqrtPrice: 1,
liquidity: 10000,
baseDecimals: 18,
quoteDecimals: 18,
},
{
isBuy: true,
Expand All @@ -35,6 +39,8 @@ const testGetSwapAmounts = () => {
lowerSqrtPrice: 1,
upperSqrtPrice: 1.2 ** 0.5,
liquidity: 0,
baseDecimals: 18,
quoteDecimals: 18,
},
{
isBuy: false,
Expand All @@ -46,6 +52,8 @@ const testGetSwapAmounts = () => {
lowerSqrtPrice: 0.8 ** 0.5,
upperSqrtPrice: 1,
liquidity: 200,
baseDecimals: 18,
quoteDecimals: 18,
},
{
isBuy: true,
Expand All @@ -57,32 +65,54 @@ const testGetSwapAmounts = () => {
lowerSqrtPrice: 1,
upperSqrtPrice: 1.2 ** 0.5,
liquidity: 200,
baseDecimals: 18,
quoteDecimals: 18,
},
{
isBuy: true,
exactInput: true,
amount: 9.26891,
swapFeeRate: 0.005,
thresholdSqrtPrice: null,
thresholdAmount: null,
lowerSqrtPrice: 0.375 ** 0.5,
upperSqrtPrice: 0.4 ** 0.5,
liquidity: 1,
baseDecimals: 18,
quoteDecimals: 6,
},
];

const baseDecimals = 18;
const quoteDecimals = 18;

for (let i = 0; i < cases.length; i++) {
const params = cases[i];
const { amountIn, amountOut, fees } = getSwapAmounts(
params.isBuy,
params.exactInput,
params.amount,
params.swapFeeRate,
params.thresholdSqrtPrice,
params.thresholdAmount,
params.lowerSqrtPrice,
params.upperSqrtPrice,
params.liquidity,
baseDecimals,
quoteDecimals
);
const { amountIn, amountOut, fees } = getSwapAmounts({
isBuy: params.isBuy,
exactInput: params.exactInput,
amount: params.amount,
swapFeeRate: params.swapFeeRate,
thresholdSqrtPrice: params.thresholdSqrtPrice,
thresholdAmount: params.thresholdAmount,
lowerSqrtPrice: params.lowerSqrtPrice,
upperSqrtPrice: params.upperSqrtPrice,
liquidity: params.liquidity,
baseDecimals: params.baseDecimals,
quoteDecimals: params.quoteDecimals,
});
console.log(`Case ${i + 1}`);
const inDecimals = params.isBuy
? params.quoteDecimals
: params.baseDecimals;
const outDecimals = params.isBuy
? params.baseDecimals
: params.quoteDecimals;
console.log({
amountIn: new Decimal(amountIn).mul(1e18).toFixed(0),
amountOut: new Decimal(amountOut).mul(1e18).toFixed(0),
fees: new Decimal(fees).mul(1e18).toFixed(0),
amountIn: new Decimal(amountIn)
.mul(new Decimal(10).pow(inDecimals))
.toFixed(0),
amountOut: new Decimal(amountOut)
.mul(new Decimal(10).pow(outDecimals))
.toFixed(0),
fees: new Decimal(fees).mul(new Decimal(10).pow(inDecimals)).toFixed(0),
});
}
};
Expand Down
16 changes: 13 additions & 3 deletions packages/replicating/src/libraries/swap_lib.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ pub fn get_swap_amounts(
};

// Compute swap amounts.
let (amount_in, amount_out, fees, _) = compute_swap_amounts(
let (net_amount_in, amount_out, fees, next_sqrt_price) = compute_swap_amounts(
start_sqrt_price,
target_sqrt_price,
position.liquidity,
Expand All @@ -58,7 +58,17 @@ pub fn get_swap_amounts(
swap_params.exact_input,
);

(amount_in + fees, amount_out, fees)
// If liquidity is sufficient to fill entire swap amount, we want to make sure the
// requested amount is fully consumed for exact input case.
let amount_in = if next_sqrt_price != target_sqrt_price
&& position.liquidity != 0
&& swap_params.exact_input {
swap_params.amount
} else {
net_amount_in + fees
};

(amount_in, amount_out, fees)
}

// Compute amounts swapped and new price after swapping between two prices.
Expand All @@ -72,7 +82,7 @@ pub fn get_swap_amounts(
// * `exact_input` - whether swap amount is exact input or output
//
// # Returns
// * `amount_in` - amount of tokens swapped in
// * `net_amount_in` - net amount of tokens swapped in
// * `amount_out` - amount of tokens swapped out
// * `fee_amount` - amount of fees
// * `next_sqrt_price` - next sqrt price
Expand Down
18 changes: 18 additions & 0 deletions packages/replicating/src/tests/libraries/test_swap_lib.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,24 @@ fn test_get_swap_amounts_ask_threshold_sqrt_price() {
);
}

#[test]
fn test_swap_amount_always_consumes_full_input_if_sufficient_liquidity() {
let swap_params = SwapParams {
is_buy: true,
amount: 9268910,
exact_input: true,
threshold_sqrt_price: Option::None(()),
threshold_amount: Option::None(()),
deadline: Option::None(()),
};
let position = PositionInfo {
lower_sqrt_price: to_e18(6100), upper_sqrt_price: to_e18(6500), liquidity: to_e18_u128(1),
};
let swap_fee_rate = 50;
let (amount_in, _, _) = get_swap_amounts(swap_params, swap_fee_rate, position);
assert(amount_in == swap_params.amount, 'Swap amount');
}

////////////////////////////////
// TESTS - compute_swap_amounts
////////////////////////////////
Expand Down
Loading
Loading