Skip to content

Commit 9ec1cec

Browse files
authored
Make SlippageIssuanceModule issuance & redemption component units getters non-view (#182)
1 parent fdefa3b commit 9ec1cec

File tree

7 files changed

+88
-26
lines changed

7 files changed

+88
-26
lines changed

contracts/interfaces/IModuleIssuanceHookV2.sol

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,14 @@ import { ISetToken } from "./ISetToken.sol";
2626
* CHANGELOG:
2727
* - Added a module level issue hook that can be used to set state ahead of component level
2828
* issue hooks
29-
* - Added view function that return expected positional adjustments during issue/redeem to
29+
* - Added non-view getter that returns expected positional adjustments during issue/redeem to
3030
* the issuance module in order to give more accurate token flow information
3131
*/
3232
interface IModuleIssuanceHookV2 {
3333

3434
function moduleIssueHook(ISetToken _setToken, uint256 _setTokenQuantity) external;
3535
function moduleRedeemHook(ISetToken _setToken, uint256 _setTokenQuantity) external;
36-
36+
3737
function componentIssueHook(
3838
ISetToken _setToken,
3939
uint256 _setTokenQuantity,
@@ -53,26 +53,30 @@ interface IModuleIssuanceHookV2 {
5353
* components array (i.e. if debt is greater than current debt position unit return negative number).
5454
* Each entry in the returned arrays should index to the same component in the SetToken's components
5555
* array (called using getComponents()).
56+
*
57+
* NOTE: This getter is non-view to allow module hooks to determine units by simulating state changes in
58+
* an external protocol and reverting. It should only be called by off-chain methods via static call.
5659
*/
5760
function getIssuanceAdjustments(
5861
ISetToken _setToken,
5962
uint256 _setTokenQuantity
6063
)
6164
external
62-
view
6365
returns (int256[] memory, int256[] memory);
6466

6567
/**
6668
* Adjustments should return the NET CHANGE in POSITION UNITS for each component in the SetToken's
6769
* components array (i.e. if debt is greater than current debt position unit return negative number).
6870
* Each entry in the returned arrays should index to the same component in the SetToken's components
6971
* array (called using getComponents()).
72+
*
73+
* NOTE: This getter is non-view to allow module hooks to determine units by simulating state changes in
74+
* an external protocol and reverting. It should only be called by off-chain methods via static call.
7075
*/
7176
function getRedemptionAdjustments(
7277
ISetToken _setToken,
7378
uint256 _setTokenQuantity
7479
)
7580
external
76-
view
7781
returns (int256[] memory, int256[] memory);
7882
}

contracts/protocol/modules/PerpV2LeverageModule.sol

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ import { IQuoter } from "../../interfaces/external/perp-v2/IQuoter.sol";
3636
import { IMarketRegistry } from "../../interfaces/external/perp-v2/IMarketRegistry.sol";
3737
import { IController } from "../../interfaces/IController.sol";
3838
import { IDebtIssuanceModule } from "../../interfaces/IDebtIssuanceModule.sol";
39-
import { IModuleIssuanceHook } from "../../interfaces/IModuleIssuanceHook.sol";
39+
import { IModuleIssuanceHookV2 } from "../../interfaces/IModuleIssuanceHookV2.sol";
4040
import { ISetToken } from "../../interfaces/ISetToken.sol";
4141
import { ModuleBase } from "../lib/ModuleBase.sol";
4242
import { SetTokenAccessible } from "../lib/SetTokenAccessible.sol";
@@ -60,7 +60,7 @@ import { UnitConversionUtils } from "../../lib/UnitConversionUtils.sol";
6060
* NOTE: The external position unit is only updated on an as-needed basis during issuance/redemption. It does not reflect the current
6161
* value of the Set's perpetual position. The current value can be calculated from getPositionNotionalInfo.
6262
*/
63-
contract PerpV2LeverageModule is ModuleBase, ReentrancyGuard, Ownable, SetTokenAccessible, IModuleIssuanceHook {
63+
contract PerpV2LeverageModule is ModuleBase, ReentrancyGuard, Ownable, SetTokenAccessible, IModuleIssuanceHookV2 {
6464
using PerpV2 for ISetToken;
6565
using PreciseUnitMath for int256;
6666
using SignedSafeMath for int256;
@@ -546,6 +546,7 @@ contract PerpV2LeverageModule is ModuleBase, ReentrancyGuard, Ownable, SetTokenA
546546
uint256 _setTokenQuantity
547547
)
548548
external
549+
override
549550
returns (int256[] memory, int256[] memory)
550551
{
551552
address[] memory components = _setToken.getComponents();
@@ -573,6 +574,7 @@ contract PerpV2LeverageModule is ModuleBase, ReentrancyGuard, Ownable, SetTokenA
573574
uint256 _setTokenQuantity
574575
)
575576
external
577+
override
576578
returns (int256[] memory, int256[] memory _)
577579
{
578580
address[] memory components = _setToken.getComponents();

contracts/protocol/modules/SlippageIssuanceModule.sol

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -184,27 +184,26 @@ contract SlippageIssuanceModule is DebtIssuanceModule {
184184
);
185185
}
186186

187-
/* ============ External View Functions ============ */
188-
189187
/**
190188
* Calculates the amount of each component needed to collateralize passed issue quantity plus fees of Sets as well as amount of debt
191-
* that will be returned to caller. Overrides inherited function to take into account position updates from pre action module hooks.
189+
* that will be returned to caller. Takes into account position updates from pre action module hooks.
192190
* (manager hooks not included).
193191
*
192+
* NOTE: This getter is non-view to allow module hooks to determine units by simulating state changes in an external protocol and
193+
* reverting. It should only be called by off-chain methods via static call.
194+
*
194195
* @param _setToken Instance of the SetToken to issue
195196
* @param _quantity Amount of Sets to be issued
196197
*
197198
* @return address[] Array of component addresses making up the Set
198199
* @return uint256[] Array of equity notional amounts of each component, respectively, represented as uint256
199200
* @return uint256[] Array of debt notional amounts of each component, respectively, represented as uint256
200201
*/
201-
function getRequiredComponentIssuanceUnits(
202+
function getRequiredComponentIssuanceUnitsOffChain(
202203
ISetToken _setToken,
203204
uint256 _quantity
204205
)
205206
external
206-
view
207-
override
208207
returns (address[] memory, uint256[] memory, uint256[] memory)
209208
{
210209
bool isIssue = true;
@@ -229,7 +228,10 @@ contract SlippageIssuanceModule is DebtIssuanceModule {
229228

230229
/**
231230
* Calculates the amount of each component that will be returned on redemption net of fees as well as how much debt needs to be paid down to
232-
* redeem. Overrides inherited function to take into account position updates from pre action module hooks (manager hooks not included).
231+
* redeem. Takes into account position updates from pre action module hooks (manager hooks not included).
232+
*
233+
* NOTE: This getter is non-view to allow module hooks to determine units by simulating state changes in an external protocol and
234+
* reverting. It should only be called by off-chain methods via static call.
233235
*
234236
* @param _setToken Instance of the SetToken to issue
235237
* @param _quantity Amount of Sets to be redeemed
@@ -238,13 +240,11 @@ contract SlippageIssuanceModule is DebtIssuanceModule {
238240
* @return uint256[] Array of equity notional amounts of each component, respectively, represented as uint256
239241
* @return uint256[] Array of debt notional amounts of each component, respectively, represented as uint256
240242
*/
241-
function getRequiredComponentRedemptionUnits(
243+
function getRequiredComponentRedemptionUnitsOffChain(
242244
ISetToken _setToken,
243245
uint256 _quantity
244246
)
245247
external
246-
view
247-
override
248248
returns (address[] memory, uint256[] memory, uint256[] memory)
249249
{
250250
bool isIssue = false;
@@ -348,7 +348,6 @@ contract SlippageIssuanceModule is DebtIssuanceModule {
348348
bool _isIssue
349349
)
350350
internal
351-
view
352351
returns (int256[] memory, int256[] memory)
353352
{
354353
uint256 componentsLength = _setToken.getComponents().length;

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@setprotocol/set-protocol-v2",
3-
"version": "0.1.6",
3+
"version": "0.1.8",
44
"description": "",
55
"main": "dist",
66
"types": "dist/types",
@@ -17,7 +17,7 @@
1717
"scripts": {
1818
"build": "yarn clean && yarn compile && yarn build:typechain",
1919
"build:npm": "yarn clean && yarn compile:npm && yarn build:typechain",
20-
"build:typechain": "yarn typechain && yarn transpile-dist && cp -rf typechain dist",
20+
"build:typechain": "yarn typechain && yarn transpile-dist",
2121
"chain": "npx hardhat node",
2222
"clean": "rm -rf coverage.json .coverage_cache .coverage_contracts cache coverage typechain artifacts dist",
2323
"compile": "npx hardhat compile",
@@ -121,4 +121,4 @@
121121
"yarn lint-ts --fix"
122122
]
123123
}
124-
}
124+
}

test/integration/perpV2LeverageSlippageIssuance.spec.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,30 @@ describe("PerpV2LeverageSlippageIssuance", () => {
424424
const expectedCollateralBalance = toUSDCDecimals(initialCollateralBalance).add(feeAdjustedTransferIn);
425425
expect(toUSDCDecimals(finalCollateralBalance)).to.be.closeTo(expectedCollateralBalance, 2);
426426
});
427+
428+
it("should get required component issuance units correctly", async () => {
429+
const issueQuantityWithFees = (await slippageIssuanceModule.calculateTotalFees(
430+
subjectSetToken,
431+
subjectQuantity,
432+
true
433+
))[0];
434+
435+
const externalPositionUnit = preciseDiv(usdcTransferInQuantity, subjectQuantity);
436+
const feeAdjustedTransferIn = preciseMul(issueQuantityWithFees, externalPositionUnit);
437+
438+
const [components, equityFlows, debtFlows] = await slippageIssuanceModule.callStatic.getRequiredComponentIssuanceUnitsOffChain(
439+
subjectSetToken,
440+
subjectQuantity
441+
);
442+
443+
const expectedComponents = await setToken.getComponents();
444+
const expectedEquityFlows = [feeAdjustedTransferIn];
445+
const expectedDebtFlows = [ZERO];
446+
447+
expect(expectedComponents[0]).to.eq(components[0]);
448+
expect(expectedEquityFlows[0]).to.be.closeTo(equityFlows[0], 50);
449+
expect(expectedDebtFlows[0]).to.eq(debtFlows[0]);
450+
});
427451
});
428452

429453
describe("when minting multiple sets", () => {
@@ -904,6 +928,32 @@ describe("PerpV2LeverageSlippageIssuance", () => {
904928
expect(finalBaseBalance).eq(expectedBaseBalance);
905929
});
906930

931+
it("should get required component redemption units correctly", async () => {
932+
const issueQuantityWithFees = (await slippageIssuanceModule.calculateTotalFees(
933+
subjectSetToken,
934+
subjectQuantity,
935+
false
936+
))[0];
937+
938+
const externalPositionUnit = preciseDiv(usdcTransferOutQuantity, subjectQuantity);
939+
const feeAdjustedTransferOut = preciseMul(issueQuantityWithFees, externalPositionUnit);
940+
941+
const [components, equityFlows, debtFlows] = await slippageIssuanceModule
942+
.callStatic
943+
.getRequiredComponentRedemptionUnitsOffChain(
944+
subjectSetToken,
945+
subjectQuantity
946+
);
947+
948+
const expectedComponents = await setToken.getComponents();
949+
const expectedEquityFlows = [feeAdjustedTransferOut];
950+
const expectedDebtFlows = [ZERO];
951+
952+
expect(expectedComponents[0]).to.eq(components[0]);
953+
expect(expectedEquityFlows[0]).to.be.closeTo(equityFlows[0], 50);
954+
expect(expectedDebtFlows[0]).to.eq(debtFlows[0]);
955+
});
956+
907957
// This is slightly off ... over a tenth of a penny.
908958
it.skip("should not incur a premium", async () => {
909959
const redeemQuantityWithFees = (await slippageIssuanceModule.calculateTotalFees(

test/protocol/modules/slippageIssuanceModule.spec.ts

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ describe("SlippageIssuanceModule", () => {
105105
await debtModule.connect(manager.wallet).initialize(setToken.address);
106106
});
107107

108-
describe("#getRequiredComponentIssuanceUnits", async () => {
108+
describe("#getRequiredComponentIssuanceUnitsOffChain", async () => {
109109
let subjectSetToken: Address;
110110
let subjectQuantity: BigNumber;
111111

@@ -119,7 +119,7 @@ describe("SlippageIssuanceModule", () => {
119119
});
120120

121121
async function subject(): Promise<any> {
122-
return slippageIssuance.getRequiredComponentIssuanceUnits(
122+
return slippageIssuance.callStatic.getRequiredComponentIssuanceUnitsOffChain(
123123
subjectSetToken,
124124
subjectQuantity
125125
);
@@ -287,7 +287,7 @@ describe("SlippageIssuanceModule", () => {
287287
});
288288
});
289289

290-
describe("#getRequiredComponentRedemptionUnits", async () => {
290+
describe("#getRequiredComponentRedemptionUnitsOffChain", async () => {
291291
let subjectSetToken: Address;
292292
let subjectQuantity: BigNumber;
293293

@@ -301,7 +301,7 @@ describe("SlippageIssuanceModule", () => {
301301
});
302302

303303
async function subject(): Promise<any> {
304-
return slippageIssuance.getRequiredComponentRedemptionUnits(
304+
return slippageIssuance.callStatic.getRequiredComponentRedemptionUnitsOffChain(
305305
subjectSetToken,
306306
subjectQuantity
307307
);
@@ -474,7 +474,10 @@ describe("SlippageIssuanceModule", () => {
474474
await debtModule.addDebt(setToken.address, setup.dai.address, debtUnits);
475475
await setup.dai.transfer(debtModule.address, ether(100.5));
476476

477-
const [, equityFlows ] = await slippageIssuance.getRequiredComponentIssuanceUnits(setToken.address, ether(1));
477+
const [, equityFlows ] = await slippageIssuance
478+
.callStatic
479+
.getRequiredComponentIssuanceUnitsOffChain(setToken.address, ether(1));
480+
478481
await setup.weth.approve(slippageIssuance.address, equityFlows[0].mul(ether(1.005)));
479482

480483
subjectSetToken = setToken.address;
@@ -816,7 +819,10 @@ describe("SlippageIssuanceModule", () => {
816819
await debtModule.addDebt(setToken.address, setup.dai.address, debtUnits);
817820
await setup.dai.transfer(debtModule.address, ether(100.5));
818821

819-
const [, equityFlows ] = await slippageIssuance.getRequiredComponentRedemptionUnits(setToken.address, ether(1));
822+
const [, equityFlows ] = await slippageIssuance
823+
.callStatic
824+
.getRequiredComponentRedemptionUnitsOffChain(setToken.address, ether(1));
825+
820826
await setup.weth.approve(slippageIssuance.address, equityFlows[0].mul(ether(1.005)));
821827

822828
await slippageIssuance.issue(setToken.address, ether(1), owner.address);

tsconfig.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
"outDir": "dist",
99
"resolveJsonModule": true,
1010
"declaration": true,
11+
"declarationDir": "./dist/types",
1112
"declarationMap": true,
1213
"sourceMap": true,
1314
"baseUrl": ".",

0 commit comments

Comments
 (0)