Skip to content

Commit 7780963

Browse files
author
Sachin
authored
fix(PerpV2LeverageModuleV2): Round up during withdraw (#249)
* Fix: round up during withdraw * Add unit test * Fix failing tests
1 parent 64822a8 commit 7780963

File tree

4 files changed

+37
-19
lines changed

4 files changed

+37
-19
lines changed

contracts/protocol/modules/v2/PerpV2LeverageModuleV2.sol

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -876,7 +876,12 @@ contract PerpV2LeverageModuleV2 is ModuleBaseV2, ReentrancyGuard, Ownable, SetTo
876876
returns (uint256)
877877
{
878878
uint256 initialCollateralPositionBalance = collateralToken.balanceOf(address(_setToken));
879-
uint256 collateralNotionalQuantity = _collateralQuantityUnits.preciseMul(_setToken.totalSupply());
879+
// Round up to calculate notional, so that we make atleast `_collateralQuantityUnits` position unit after withdraw.
880+
// Example, let totalSupply = 1.005e18, _collateralQuantityUnits = 13159, then
881+
// collateralNotionalQuantity = 13159 * 1.005e18 / 1e18 = 13225 (13224.795 rounded up)
882+
// We withdraw 13225 from Perp and make a position unit from it. So newPositionUnit = (13225 / 1.005e18) * 1e18
883+
// = 13159 (13159.2039801 rounded down)
884+
uint256 collateralNotionalQuantity = _collateralQuantityUnits.preciseMulCeil(_setToken.totalSupply());
880885

881886
_withdraw(_setToken, collateralNotionalQuantity);
882887

test/integration/perpV2BasisTradingSlippageIssuance.spec.ts

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -778,7 +778,7 @@ describe("PerpV2BasisTradingSlippageIssuance", () => {
778778
expect(finalOwnerUSDCBalance).to.be.closeTo(expectedUSDCBalance, 1);
779779
});
780780

781-
it("should remove the module when dust is in the account and be able to add module back", async () => {
781+
it("should remove the module and be able to add module back", async () => {
782782
// Redeem to `1`
783783
await subject();
784784

@@ -811,18 +811,12 @@ describe("PerpV2BasisTradingSlippageIssuance", () => {
811811
.connect(owner.wallet)
812812
.withdraw(subjectSetToken, freeCollateralPositionUnit);
813813

814-
const {
815-
collateralBalance: finalCollateralBalance
816-
} = await perpBasisTradingModule.getAccountInfo(subjectSetToken);
817-
818-
819814
/// Remove module
820815
await setToken.removeModule(perpBasisTradingModule.address);
821816
const finalModules = await setToken.getModules();
822817

823818
expect(finalModules.includes(perpBasisTradingModule.address)).eq(false);
824819
expect(positionInfo.length).eq(0);
825-
expect(toUSDCDecimals(finalCollateralBalance)).eq(1); // <-- DUST
826820

827821
// Restore module
828822
await setToken.connect(owner.wallet).addModule(perpBasisTradingModule.address);

test/integration/perpV2LeverageV2SlippageIssuance.spec.ts

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1129,7 +1129,7 @@ describe("PerpV2LeverageSlippageIssuance", () => {
11291129
expect(finalOwnerUSDCBalance).to.be.closeTo(expectedUSDCBalance, 1);
11301130
});
11311131

1132-
it("should remove the module when dust is in the account and be able to add module back", async () => {
1132+
it("should remove the module and be able to add module back", async () => {
11331133
// Redeem to `1`
11341134
await subject();
11351135

@@ -1162,18 +1162,12 @@ describe("PerpV2LeverageSlippageIssuance", () => {
11621162
.connect(owner.wallet)
11631163
.withdraw(subjectSetToken, freeCollateralPositionUnit);
11641164

1165-
const {
1166-
collateralBalance: finalCollateralBalance
1167-
} = await perpLeverageModule.getAccountInfo(subjectSetToken);
1168-
1169-
11701165
/// Remove module
11711166
await setToken.removeModule(perpLeverageModule.address);
11721167
const finalModules = await setToken.getModules();
11731168

11741169
expect(finalModules.includes(perpLeverageModule.address)).eq(false);
11751170
expect(positionInfo.length).eq(0);
1176-
expect(toUSDCDecimals(finalCollateralBalance)).eq(1); // <-- DUST
11771171

11781172
// Restore module
11791173
await setToken.connect(owner.wallet).addModule(perpLeverageModule.address);

test/protocol/modules/v2/perpV2LeverageModuleV2.spec.ts

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1505,13 +1505,16 @@ describe("PerpV2LeverageModuleV2", () => {
15051505
});
15061506

15071507
describe("#withdraw", () => {
1508-
let depositQuantity: BigNumber;
1508+
const depositQuantity: BigNumber = usdcUnits(10);
15091509
let subjectSetToken: SetToken;
15101510
let subjectWithdrawQuantity: BigNumber;
15111511
let subjectCaller: Account;
15121512
let isInitialized: boolean;
15131513

1514-
const initializeContracts = async () => {
1514+
const initializeContracts = async (
1515+
issueQuantity: BigNumber = ether(2),
1516+
depositQuantity: BigNumber = usdcUnits(10)
1517+
) => {
15151518
subjectSetToken = await setup.createSetToken(
15161519
[usdc.address],
15171520
[usdcUnits(100)],
@@ -1524,13 +1527,11 @@ describe("PerpV2LeverageModuleV2", () => {
15241527
if (isInitialized) {
15251528
await perpLeverageModule.initialize(subjectSetToken.address);
15261529

1527-
const issueQuantity = ether(2);
15281530
await usdc.approve(setup.issuanceModule.address, usdcUnits(1000));
15291531
await setup.issuanceModule.initialize(subjectSetToken.address, ADDRESS_ZERO);
15301532
await setup.issuanceModule.issue(subjectSetToken.address, issueQuantity, owner.address);
15311533

15321534
// Deposit 10 USDC
1533-
depositQuantity = usdcUnits(10);
15341535
await perpLeverageModule
15351536
.connect(owner.wallet)
15361537
.deposit(subjectSetToken.address, depositQuantity);
@@ -1713,6 +1714,30 @@ describe("PerpV2LeverageModuleV2", () => {
17131714
await expect(subject()).to.be.revertedWith("Must be the SetToken manager");
17141715
});
17151716
});
1717+
1718+
describe("when rounding up notional amount is required", async () => {
1719+
beforeEach(async () => {
1720+
isInitialized = true;
1721+
const issueQuantity = ether(1.005);
1722+
const depositQuantity = usdcUnits(100); // deposit all
1723+
await initializeContracts(issueQuantity, depositQuantity);
1724+
1725+
subjectWithdrawQuantity = BigNumber.from(13159);
1726+
});
1727+
1728+
it("should update USDC default position unit", async () => {
1729+
await subject();
1730+
1731+
const defaultPositionUnit = await subjectSetToken.getDefaultPositionRealUnit(usdc.address);
1732+
1733+
// Round up to calculate notional, so that we make atleast `_collateralQuantityUnits` position unit after withdraw.
1734+
// Example, let totalSupply = 1.005e18, _collateralQuantityUnits = 13159, then
1735+
// collateralNotionalQuantity = 13159 * 1.005e18 / 1e18 = 13225 (13224.795 rounded up)
1736+
// We withdraw 13225 from Perp and make a position unit from it. So newPositionUnit = (13225 / 1.005e18) * 1e18
1737+
// = 13159 (13159.2039801 rounded down)
1738+
expect(defaultPositionUnit).to.eq(subjectWithdrawQuantity); // 13159
1739+
});
1740+
});
17161741
});
17171742

17181743
describe("when module is not initialized", async () => {

0 commit comments

Comments
 (0)