Skip to content

Commit 93dc0a0

Browse files
authored
fix deposit ratio bug (#505)
1 parent a6ccd6e commit 93dc0a0

2 files changed

Lines changed: 133 additions & 18 deletions

File tree

tokens/token-swap/anchor/programs/token-swap/src/instructions/deposit_liquidity.rs

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -37,25 +37,30 @@ pub fn deposit_liquidity(
3737
// Add as is if there is no liquidity
3838
(amount_a, amount_b)
3939
} else {
40-
let ratio = I64F64::from_num(pool_a.amount)
41-
.checked_mul(I64F64::from_num(pool_b.amount))
40+
// u128 is enough precision here
41+
let amount_a_u128 = amount_a as u128;
42+
let amount_b_u128 = amount_b as u128;
43+
let pool_a_u128 = pool_a.amount as u128;
44+
let pool_b_u128 = pool_b.amount as u128;
45+
46+
// Calculate the amount of B required if we deposit all of A provided
47+
let amount_b_required = amount_a_u128
48+
.checked_mul(pool_b_u128)
49+
.unwrap()
50+
.checked_div(pool_a_u128)
4251
.unwrap();
43-
if pool_a.amount > pool_b.amount {
44-
(
45-
I64F64::from_num(amount_b)
46-
.checked_mul(ratio)
47-
.unwrap()
48-
.to_num::<u64>(),
49-
amount_b,
50-
)
52+
53+
if amount_b_required <= amount_b_u128 {
54+
// We have enough B to match the A provided
55+
(amount_a, amount_b_required as u64)
5156
} else {
52-
(
53-
amount_a,
54-
I64F64::from_num(amount_a)
55-
.checked_div(ratio)
56-
.unwrap()
57-
.to_num::<u64>(),
58-
)
57+
// We don't have enough B, so we must limit by B and calculate A required
58+
let amount_a_required = amount_b_u128
59+
.checked_mul(pool_a_u128)
60+
.unwrap()
61+
.checked_div(pool_b_u128)
62+
.unwrap();
63+
(amount_a_required as u64, amount_b)
5964
}
6065
};
6166

tokens/token-swap/anchor/tests/deposit-liquidity.ts

Lines changed: 111 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,4 +71,114 @@ describe("Deposit liquidity", () => {
7171
const depositTokenAccountB = await connection.getTokenAccountBalance(values.holderAccountB);
7272
expect(depositTokenAccountB.value.amount).to.equal(values.defaultSupply.sub(values.depositAmountA).toString());
7373
});
74-
});
74+
75+
it('Deposit with existing liquidity (same ratio)', async () => {
76+
// 1. Initial Deposit
77+
await program.methods
78+
.depositLiquidity(values.depositAmountA, values.depositAmountA)
79+
.accounts({
80+
pool: values.poolKey,
81+
poolAuthority: values.poolAuthority,
82+
depositor: values.admin.publicKey,
83+
mintLiquidity: values.mintLiquidity,
84+
mintA: values.mintAKeypair.publicKey,
85+
mintB: values.mintBKeypair.publicKey,
86+
poolAccountA: values.poolAccountA,
87+
poolAccountB: values.poolAccountB,
88+
depositorAccountLiquidity: values.liquidityAccount,
89+
depositorAccountA: values.holderAccountA,
90+
depositorAccountB: values.holderAccountB,
91+
})
92+
.signers([values.admin])
93+
.rpc({ skipPreflight: true });
94+
95+
// 2. Second Deposit
96+
const secondDepositAmount = new anchor.BN(100000);
97+
await program.methods
98+
.depositLiquidity(secondDepositAmount, secondDepositAmount)
99+
.accounts({
100+
pool: values.poolKey,
101+
poolAuthority: values.poolAuthority,
102+
depositor: values.admin.publicKey,
103+
mintLiquidity: values.mintLiquidity,
104+
mintA: values.mintAKeypair.publicKey,
105+
mintB: values.mintBKeypair.publicKey,
106+
poolAccountA: values.poolAccountA,
107+
poolAccountB: values.poolAccountB,
108+
depositorAccountLiquidity: values.liquidityAccount,
109+
depositorAccountA: values.holderAccountA,
110+
depositorAccountB: values.holderAccountB,
111+
})
112+
.signers([values.admin])
113+
.rpc({ skipPreflight: true });
114+
115+
const poolAccountA = await connection.getTokenAccountBalance(values.poolAccountA);
116+
expect(poolAccountA.value.amount).to.equal(values.depositAmountA.add(secondDepositAmount).toString());
117+
});
118+
119+
it('Deposit with different ratio', async () => {
120+
// 1. Initial Deposit with 1:5 ratio
121+
// Pool A: 1,000,000
122+
// Pool B: 5,000,000
123+
const initialAmountA = new anchor.BN(1_000_000);
124+
const initialAmountB = new anchor.BN(5_000_000);
125+
126+
await program.methods
127+
.depositLiquidity(initialAmountA, initialAmountB)
128+
.accounts({
129+
pool: values.poolKey,
130+
poolAuthority: values.poolAuthority,
131+
depositor: values.admin.publicKey,
132+
mintLiquidity: values.mintLiquidity,
133+
mintA: values.mintAKeypair.publicKey,
134+
mintB: values.mintBKeypair.publicKey,
135+
poolAccountA: values.poolAccountA,
136+
poolAccountB: values.poolAccountB,
137+
depositorAccountLiquidity: values.liquidityAccount,
138+
depositorAccountA: values.holderAccountA,
139+
depositorAccountB: values.holderAccountB,
140+
})
141+
.signers([values.admin])
142+
.rpc({ skipPreflight: true });
143+
144+
// 2. Second Deposit with mismatched input
145+
// Input A: 500,000
146+
// Input B: 500,000
147+
// Logic:
148+
// - 500k A requires 2.5M B. (User only provided 500k B).
149+
// - 500k B requires 100k A. (User provided 500k A).
150+
// Result: Deposit 100k A and 500k B.
151+
const secondDepositA = new anchor.BN(500000);
152+
const secondDepositBInput = new anchor.BN(500000);
153+
154+
await program.methods
155+
.depositLiquidity(secondDepositA, secondDepositBInput)
156+
.accounts({
157+
pool: values.poolKey,
158+
poolAuthority: values.poolAuthority,
159+
depositor: values.admin.publicKey,
160+
mintLiquidity: values.mintLiquidity,
161+
mintA: values.mintAKeypair.publicKey,
162+
mintB: values.mintBKeypair.publicKey,
163+
poolAccountA: values.poolAccountA,
164+
poolAccountB: values.poolAccountB,
165+
depositorAccountLiquidity: values.liquidityAccount,
166+
depositorAccountA: values.holderAccountA,
167+
depositorAccountB: values.holderAccountB,
168+
})
169+
.signers([values.admin])
170+
.rpc({ skipPreflight: true });
171+
172+
// 3. Verify Balances
173+
const poolAccountA = await connection.getTokenAccountBalance(values.poolAccountA);
174+
const poolAccountB = await connection.getTokenAccountBalance(values.poolAccountB);
175+
176+
// Total A: 1,000,000 + 100,000 = 1,100,000
177+
// We expect 100,000 A to be deposited.
178+
const expectedAdditionalA = secondDepositBInput.mul(initialAmountA).div(initialAmountB); // 500k * (1M/5M) = 100k
179+
expect(poolAccountA.value.amount).to.equal(initialAmountA.add(expectedAdditionalA).toString());
180+
181+
// Total B: 5,000,000 + 500,000 = 5,500,000
182+
expect(poolAccountB.value.amount).to.equal(initialAmountB.add(secondDepositBInput).toString());
183+
});
184+
});

0 commit comments

Comments
 (0)