Skip to content
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
44 changes: 39 additions & 5 deletions contracts/protocol/modules/PerpV2LeverageModule.sol
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ contract PerpV2LeverageModule is ModuleBase, ReentrancyGuard, Ownable, IModuleIs
/* ============ Events ============ */

/**
* @dev Emitted on lever
* @dev Emitted on trade
* @param _setToken Instance of SetToken
* @param _baseToken Virtual token minted by the Perp protocol
* @param _deltaBase Change in baseToken position size resulting from trade
Expand All @@ -116,6 +116,30 @@ contract PerpV2LeverageModule is ModuleBase, ReentrancyGuard, Ownable, IModuleIs
bool _isBuy
);

/**
* @dev Emitted on deposit (not issue or redeeem)
* @param _setToken Instance of SetToken
* @param _collateralToken Token being deposited as collateral (USDC)
* @param _amountDeposited Amount of collateral being deposited into Perp
*/
event CollateralDeposited(
ISetToken indexed _setToken,
IERC20 _collateralToken,
uint256 _amountDeposited
);

/**
* @dev Emitted on withdraw (not issue or redeeem)
* @param _setToken Instance of SetToken
* @param _collateralToken Token being withdrawn as collateral (USDC)
* @param _amountWithdrawn Amount of collateral being withdrawn from Perp
*/
event CollateralWithdrawn(
ISetToken indexed _setToken,
IERC20 _collateralToken,
uint256 _amountWithdrawn
);

/**
* @dev Emitted on updateAllowedSetToken()
* @param _setToken SetToken being whose allowance to initialize this module is being updated
Expand Down Expand Up @@ -298,7 +322,9 @@ contract PerpV2LeverageModule is ModuleBase, ReentrancyGuard, Ownable, IModuleIs
require(_collateralQuantityUnits > 0, "Deposit amount is 0");
require(_setToken.totalSupply() > 0, "SetToken supply is 0");

_depositAndUpdateState(_setToken, _collateralQuantityUnits);
uint256 notionalDepositedQuantity = _depositAndUpdatePositions(_setToken, _collateralQuantityUnits);

emit CollateralDeposited(_setToken, collateralToken, notionalDepositedQuantity);
}

/**
Expand All @@ -323,7 +349,9 @@ contract PerpV2LeverageModule is ModuleBase, ReentrancyGuard, Ownable, IModuleIs
require(_collateralQuantityUnits > 0, "Withdraw amount is 0");
require(_setToken.totalSupply() > 0, "SetToken supply is 0");

_withdrawAndUpdateState(_setToken, _collateralQuantityUnits);
uint256 notionalWithdrawnQuantity = _withdrawAndUpdatePositions(_setToken, _collateralQuantityUnits);

emit CollateralWithdrawn(_setToken, collateralToken, notionalWithdrawnQuantity);
}

/**
Expand Down Expand Up @@ -890,11 +918,12 @@ contract PerpV2LeverageModule is ModuleBase, ReentrancyGuard, Ownable, IModuleIs
* NOTE: This flow is only used when invoking the external `deposit` function - it converts collateral
* quantity units into a notional quantity.
*/
function _depositAndUpdateState(
function _depositAndUpdatePositions(
ISetToken _setToken,
uint256 _collateralQuantityUnits
)
internal
returns (uint256)
{
uint256 initialCollateralPositionBalance = collateralToken.balanceOf(address(_setToken));
uint256 collateralNotionalQuantity = _collateralQuantityUnits.preciseMul(_setToken.totalSupply());
Expand All @@ -913,6 +942,8 @@ contract PerpV2LeverageModule is ModuleBase, ReentrancyGuard, Ownable, IModuleIs
_calculateExternalPositionUnit(_setToken),
""
);

return collateralNotionalQuantity;
}

/**
Expand All @@ -935,12 +966,13 @@ contract PerpV2LeverageModule is ModuleBase, ReentrancyGuard, Ownable, IModuleIs
* NOTE: This flow is only used when invoking the external `withdraw` function - it converts
* a collateral units quantity into a notional quantity before invoking withdraw.
*/
function _withdrawAndUpdateState(
function _withdrawAndUpdatePositions(
ISetToken _setToken,
uint256 _collateralQuantityUnits

)
internal
returns (uint256)
{
uint256 initialCollateralPositionBalance = collateralToken.balanceOf(address(_setToken));
uint256 collateralNotionalQuantity = _collateralQuantityUnits.preciseMul(_setToken.totalSupply());
Expand All @@ -959,6 +991,8 @@ contract PerpV2LeverageModule is ModuleBase, ReentrancyGuard, Ownable, IModuleIs
_calculateExternalPositionUnit(_setToken),
""
);

return collateralNotionalQuantity;
}

/**
Expand Down
61 changes: 40 additions & 21 deletions test/protocol/modules/perpV2LeverageModule.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ import {
import { PerpV2Fixture, SystemFixture } from "@utils/fixtures";
import { ADDRESS_ZERO, ZERO, ZERO_BYTES, MAX_UINT_256, ONE_DAY_IN_SECONDS } from "@utils/constants";
import { BigNumber } from "ethers";
// import { inspect } from "util";

const expect = getWaffleExpect();

Expand Down Expand Up @@ -990,7 +989,7 @@ describe("PerpV2LeverageModule", () => {
};

async function subject(): Promise<any> {
await perpLeverageModule
return await perpLeverageModule
.connect(subjectCaller.wallet)
.deposit(subjectSetToken.address, subjectDepositQuantity);
}
Expand Down Expand Up @@ -1053,6 +1052,16 @@ describe("PerpV2LeverageModule", () => {
expect(finalExternalPositionUnit).to.eq(expectedDefaultPosition);
});

it("should emit the correct CollateralDeposited event", async () => {
const totalSupply = await subjectSetToken.totalSupply();

await expect(subject()).to.emit(perpLeverageModule, "CollateralDeposited").withArgs(
subjectSetToken.address,
perpSetup.usdc.address,
preciseMul(subjectDepositQuantity, totalSupply)
);
});

describe("when depositing and a position exists", () => {
let baseToken: Address;

Expand Down Expand Up @@ -1255,29 +1264,29 @@ describe("PerpV2LeverageModule", () => {
await expect(subject()).to.be.revertedWith("Must be the SetToken manager");
});
});
});

describe("when total supply is 0", async () => {
let otherSetToken: SetToken;
describe("when total supply is 0", async () => {
let otherSetToken: SetToken;

cacheBeforeEach(initializeContracts);
beforeEach(initializeSubjectVariables);
cacheBeforeEach(initializeContracts);
beforeEach(initializeSubjectVariables);

beforeEach(async () => {
otherSetToken = await setup.createSetToken(
[usdc.address],
[usdcUnits(10)],
[perpLeverageModule.address, debtIssuanceMock.address]
);
await debtIssuanceMock.initialize(otherSetToken.address);
await perpLeverageModule.updateAllowedSetToken(otherSetToken.address, true);
await perpLeverageModule.connect(owner.wallet).initialize(otherSetToken.address);
beforeEach(async () => {
otherSetToken = await setup.createSetToken(
[usdc.address],
[usdcUnits(10)],
[perpLeverageModule.address, debtIssuanceMock.address]
);
await debtIssuanceMock.initialize(otherSetToken.address);
await perpLeverageModule.updateAllowedSetToken(otherSetToken.address, true);
await perpLeverageModule.connect(owner.wallet).initialize(otherSetToken.address);

subjectSetToken = otherSetToken;
});
subjectSetToken = otherSetToken;
});

it("should revert", async () => {
await expect(subject()).to.be.revertedWith("SetToken supply is 0");
it("should revert", async () => {
await expect(subject()).to.be.revertedWith("SetToken supply is 0");
});
});
});

Expand Down Expand Up @@ -1333,7 +1342,7 @@ describe("PerpV2LeverageModule", () => {
};

async function subject(): Promise<any> {
await perpLeverageModule
return await perpLeverageModule
.connect(subjectCaller.wallet)
.withdraw(subjectSetToken.address, subjectWithdrawQuantity);
}
Expand Down Expand Up @@ -1377,6 +1386,16 @@ describe("PerpV2LeverageModule", () => {
expect(finalExternalPositionUnit).to.eq(expectedExternalPositionUnit);
});

it("should emit the correct CollateralWithdrawn event", async () => {
const totalSupply = await subjectSetToken.totalSupply();

await expect(subject()).to.emit(perpLeverageModule, "CollateralWithdrawn").withArgs(
subjectSetToken.address,
perpSetup.usdc.address,
preciseMul(subjectWithdrawQuantity, totalSupply)
);
});

describe("when withdraw amount is 0", async () => {
beforeEach(() => {
subjectWithdrawQuantity = usdcUnits(0);
Expand Down