Skip to content

Commit

Permalink
fix(PerpV2BasisTradingModule): Expose getter to fetch updated settled…
Browse files Browse the repository at this point in the history
… funding (SetProtocol#244)

* Make getUpdatedSettledFunding public; Add javadcos

* Add unit tests for getUpdatedSettledFunding
  • Loading branch information
Sachin committed Apr 13, 2022
1 parent c67d48b commit f215147
Show file tree
Hide file tree
Showing 2 changed files with 152 additions and 18 deletions.
39 changes: 21 additions & 18 deletions contracts/protocol/modules/v2/PerpV2BasisTradingModule.sol
Original file line number Diff line number Diff line change
Expand Up @@ -429,7 +429,7 @@ contract PerpV2BasisTradingModule is PerpV2LeverageModuleV2 {

if (positions[_setToken].length > 0) {

uint256 updatedSettledFunding = _getUpdatedSettledFunding(_setToken);
uint256 updatedSettledFunding = getUpdatedSettledFunding(_setToken);

int256 newExternalPositionUnit = _executePositionTrades(_setToken, _setTokenQuantity, false, true);

Expand All @@ -439,31 +439,18 @@ contract PerpV2BasisTradingModule is PerpV2LeverageModuleV2 {
return _formatAdjustments(_setToken, newExternalPositionUnitNetFees);
}

/* ============ Internal Functions ============ */

/**
* @dev Updates tracked settled funding. Once funding is settled to `owedRealizedPnl` on Perpetual protocol, it is difficult to
* extract out the funding value again on-chain. This function is called in an external function and is used to track and store
* pending funding payment that is about to be settled due to subsequent logic in the external function.
*
* @param _setToken Instance of SetToken
* @return uint256 Returns the updated settled funding value
*/
function _updateSettledFunding(ISetToken _setToken) internal returns (uint256) {
uint256 newSettledFunding = _getUpdatedSettledFunding(_setToken);
settledFunding[_setToken] = newSettledFunding;
return newSettledFunding;
}

/**
* @dev Adds pending funding payment to tracked settled funding. Returns updated settled funding value.
*
* NOTE: Tracked settled funding value can not be less than zero, hence it is reset to zero if pending funding
* payment is negative and |pending funding payment| >= |settledFunding[_setToken]|.
*
* NOTE: Returned updated settled funding value is correct only for the current block since pending funding payment
* updates every block.
*
* @param _setToken Instance of SetToken
*/
function _getUpdatedSettledFunding(ISetToken _setToken) internal view returns (uint256) {
function getUpdatedSettledFunding(ISetToken _setToken) public view returns (uint256) {
// NOTE: pendingFundingPayments are represented as in the Perp system as "funding owed"
// e.g a positive number is a debt which gets subtracted from owedRealizedPnl on settlement.
// We are flipping its sign here to reflect its settlement value.
Expand All @@ -480,6 +467,22 @@ contract PerpV2BasisTradingModule is PerpV2LeverageModuleV2 {
return 0;
}

/* ============ Internal Functions ============ */

/**
* @dev Updates tracked settled funding. Once funding is settled to `owedRealizedPnl` on Perpetual protocol, it is difficult to
* extract out the funding value again on-chain. This function is called in an external function and is used to track and store
* pending funding payment that is about to be settled due to subsequent logic in the external function.
*
* @param _setToken Instance of SetToken
* @return uint256 Returns the updated settled funding value
*/
function _updateSettledFunding(ISetToken _setToken) internal returns (uint256) {
uint256 newSettledFunding = getUpdatedSettledFunding(_setToken);
settledFunding[_setToken] = newSettledFunding;
return newSettledFunding;
}

/**
* @dev Calculates manager and protocol fees on withdrawn funding amount and transfers them to
* their respective recipients (in USDC).
Expand Down
131 changes: 131 additions & 0 deletions test/protocol/modules/v2/perpV2BasisTradingModule.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1916,6 +1916,137 @@ describe("PerpV2BasisTradingModule", () => {
});
});

describe("#getUpdatedSettledFunding", async () => {
let setToken: SetToken;
let subjectSetToken: Address;

const initializeContracts = async () => {
const depositQuantity = usdcUnits(10);

setToken = await issueSetsAndDepositToPerp(depositQuantity, true);

await perpBasisTradingModule.connect(owner.wallet).trade(
setToken.address,
vETH.address,
ether(0.5),
ether(5.15)
);

// Move oracle price up and wait one day
await perpSetup.setBaseTokenOraclePrice(vETH, usdcUnits(10.2));
await increaseTimeAsync(ONE_DAY_IN_SECONDS);

await perpBasisTradingModule.connect(owner.wallet).tradeAndTrackFunding(
setToken.address,
vETH.address,
ether(0.5),
ether(5.15),
);
};

const initializeSubjectVariables = async () => {
subjectSetToken = setToken.address;
};

cacheBeforeEach(initializeContracts);
beforeEach(initializeSubjectVariables);

async function subject(): Promise<BigNumber> {
return await perpBasisTradingModule.getUpdatedSettledFunding(subjectSetToken);
}

describe("when pending funding payment is zero", async () => {
it("should return current settled funding", async () => {
const settledFundingBefore = await perpBasisTradingModule.settledFunding(subjectSetToken);
const updatedSettledFunding = await subject();

expect(settledFundingBefore).to.be.gt(ZERO);
expect(updatedSettledFunding).to.eq(settledFundingBefore);
});
});

describe("when pending funding payment is positive", async () => {
beforeEach(async () => {
// Move oracle price up and wait one day
await perpSetup.setBaseTokenOraclePrice(vETH, usdcUnits(10.5));
await increaseTimeAsync(ONE_DAY_IN_SECONDS);
});

it("should return correct updated settled funding", async () => {
const settledFundingBefore = await perpBasisTradingModule.settledFunding(subjectSetToken);
const pendingFundingPayment = (await perpSetup.exchange.getAllPendingFundingPayment(subjectSetToken)).mul(-1);
const expectedSettledFunding = settledFundingBefore.add(pendingFundingPayment);

const updatedSettledFunding = await subject();

expect(pendingFundingPayment).to.be.gt(ZERO);
expect(updatedSettledFunding).to.be.gt(settledFundingBefore);
expect(updatedSettledFunding).to.eq(expectedSettledFunding);
});
});

describe("when pending funding payment is negative", async () => {
describe("when absolute settled funding is less than absolute negative funding", async () => {
beforeEach(async () => {
// Move oracle price down and wait one day
await perpSetup.setBaseTokenOraclePrice(vETH, usdcUnits(9.5));
await increaseTimeAsync(ONE_DAY_IN_SECONDS);
});

it("verify testing conditions", async () => {
const settledFundingBefore = await perpBasisTradingModule.settledFunding(subjectSetToken);
const pendingFundingBefore = await perpSetup.exchange.getAllPendingFundingPayment(subjectSetToken);

expect(pendingFundingBefore.abs()).to.be.gt(settledFundingBefore);
});

it("should return correct updated settled funding (zero)", async () => {
const updatedSettledFunding = await subject();
expect(updatedSettledFunding).to.eq(ZERO);
});
});

describe("when absolute settled funding is greater then absolute negative pending funding", async () => {
beforeEach(async () => {
// Move oracle price up and wait one day
await perpSetup.setBaseTokenOraclePrice(vETH, usdcUnits(11));
await increaseTimeAsync(ONE_DAY_IN_SECONDS);

// Trade to accrue pending funding to tracked settled funding
await perpBasisTradingModule.connect(owner.wallet).tradeAndTrackFunding(
setToken.address,
vETH.address,
ether(1),
ether(10.15)
);

// Move oracle price down and wait one day
await perpSetup.setBaseTokenOraclePrice(vETH, usdcUnits(9.8));
await increaseTimeAsync(ONE_DAY_IN_SECONDS);
});

it("verify testing conditions", async () => {
const settledFundingBefore = await perpBasisTradingModule.settledFunding(subjectSetToken);
const pendingFundingBefore = await perpSetup.exchange.getAllPendingFundingPayment(subjectSetToken);

expect(settledFundingBefore.abs()).to.be.gt(pendingFundingBefore.abs());
});

it("should return correct updated settled funding", async () => {
const settledFundingBefore = await perpBasisTradingModule.settledFunding(subjectSetToken);
const pendingFundingPayment = (await perpSetup.exchange.getAllPendingFundingPayment(subjectSetToken)).mul(-1);
const expectedSettledFunding = settledFundingBefore.add(pendingFundingPayment);

const updatedSettledFunding = await subject();

expect(pendingFundingPayment).to.be.lt(ZERO);
expect(updatedSettledFunding).to.be.lt(settledFundingBefore);
expect(updatedSettledFunding).to.eq(expectedSettledFunding);
});
});
});
});

describe("#updateFeeRecipient", async () => {
let setToken: SetToken;
let isInitialized: boolean;
Expand Down

0 comments on commit f215147

Please sign in to comment.