Skip to content

Commit f5adeb8

Browse files
committed
Update FLI viewer for multihop
1 parent 8fcb3cf commit f5adeb8

File tree

2 files changed

+131
-5
lines changed

2 files changed

+131
-5
lines changed

contracts/viewers/FLIRebalanceViewer.sol

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,12 @@ import { PreciseUnitMath } from "../lib/PreciseUnitMath.sol";
3232
* @title FLIRebalanceViewer
3333
* @author Set Protocol
3434
*
35-
* ETHFLI Rebalance viewer that returns whether a Compound oracle update should be forced before a rebalance goes through, if no
35+
* FLI Rebalance viewer that returns whether a Compound oracle update should be forced before a rebalance goes through, if no
3636
* oracle update the type of rebalance transaction will be returned adhering to the enum specified in FlexibleLeverageStrategyAdapter
37+
*
38+
* CHANGELOG: 4/28/2021
39+
* - Enables calculating Uniswap price with any multihop route
40+
* - Lever and delever exchange data is read directly from strategy adapter and if not empty, calculate price based on encoded route
3741
*/
3842
contract FLIRebalanceViewer {
3943
using PreciseUnitMath for uint256;
@@ -163,17 +167,25 @@ contract FLIRebalanceViewer {
163167
view
164168
returns (uint256)
165169
{
166-
address[] memory path = new address[](2);
167-
path[0] = _sellAsset;
168-
path[1] = _buyAsset;
170+
bytes memory pathCalldata = _isBuyingCollateral ? strategyAdapter.getExecution().leverExchangeData : strategyAdapter.getExecution().deleverExchangeData;
171+
172+
address[] memory path;
173+
if(pathCalldata.length == 0){
174+
path = new address[](2);
175+
path[0] = _sellAsset;
176+
path[1] = _buyAsset;
177+
} else {
178+
path = abi.decode(pathCalldata, (address[]));
179+
}
169180

170181
// Returned [sellAmount, buyAmount]
171182
uint256[] memory flows = _isBuyingCollateral ? uniswapRouter.getAmountsIn(_tradeSize, path) : uniswapRouter.getAmountsOut(_tradeSize, path);
172183

173184
uint256 buyDecimals = uint256(10)**ERC20(_buyAsset).decimals();
174185
uint256 sellDecimals = uint256(10)**ERC20(_sellAsset).decimals();
175186

176-
return flows[0].preciseDiv(sellDecimals).preciseDiv(flows[1].preciseDiv(buyDecimals));
187+
uint256 lastIndex = path.length.sub(1);
188+
return flows[0].preciseDiv(sellDecimals).preciseDiv(flows[lastIndex].preciseDiv(buyDecimals));
177189
}
178190

179191
/**

test/viewers/fliRebalanceViewer.spec.ts

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import "module-alias/register";
22
import { BigNumber } from "@ethersproject/bignumber";
3+
import { defaultAbiCoder } from "ethers/lib/utils";
34

45
import {
56
Account,
@@ -14,6 +15,7 @@ import { CompoundLeverageModule, DebtIssuanceModule, SetToken } from "@utils/con
1415
import { CEther, CERc20 } from "@utils/contracts/compound";
1516
import DeployHelper from "@utils/deploys";
1617
import {
18+
bitcoin,
1719
cacheBeforeEach,
1820
ether,
1921
getAccounts,
@@ -103,6 +105,7 @@ describe("FLIRebalanceViewer", () => {
103105

104106
await setV2Setup.weth.connect(owner.wallet).approve(uniswapSetup.router.address, MAX_UINT_256);
105107
await setV2Setup.usdc.connect(owner.wallet).approve(uniswapSetup.router.address, MAX_UINT_256);
108+
await setV2Setup.wbtc.connect(owner.wallet).approve(uniswapSetup.router.address, MAX_UINT_256);
106109
await uniswapSetup.router.addLiquidity(
107110
setV2Setup.weth.address,
108111
setV2Setup.usdc.address,
@@ -113,6 +116,26 @@ describe("FLIRebalanceViewer", () => {
113116
owner.address,
114117
MAX_UINT_256
115118
);
119+
await uniswapSetup.router.addLiquidity(
120+
setV2Setup.wbtc.address,
121+
setV2Setup.usdc.address,
122+
bitcoin(100),
123+
usdc(5000000),
124+
bitcoin(99),
125+
usdc(4000000),
126+
owner.address,
127+
MAX_UINT_256
128+
);
129+
await uniswapSetup.router.addLiquidity(
130+
setV2Setup.wbtc.address,
131+
setV2Setup.weth.address,
132+
bitcoin(100),
133+
ether(5000),
134+
bitcoin(99),
135+
ether(4000),
136+
owner.address,
137+
MAX_UINT_256
138+
);
116139

117140
// Mint cTokens
118141
await setV2Setup.usdc.approve(cUSDC.address, ether(100000));
@@ -581,5 +604,96 @@ describe("FLIRebalanceViewer", () => {
581604
});
582605
});
583606
});
607+
608+
context.only("when there is a multihop trade", async () => {
609+
describe("when above max leverage ratio but below incentivized leverage ratio", async () => {
610+
beforeEach(async () => {
611+
await compoundSetup.priceOracle.setUnderlyingPrice(cEther.address, ether(850));
612+
613+
await flexibleLeverageStrategyAdapter.setExecutionSettings(
614+
{
615+
unutilizedLeveragePercentage: execution.unutilizedLeveragePercentage,
616+
twapMaxTradeSize: execution.twapMaxTradeSize,
617+
twapCooldownPeriod: execution.twapCooldownPeriod,
618+
slippageTolerance: execution.slippageTolerance,
619+
exchangeName: "UniswapTradeAdapter",
620+
leverExchangeData: EMPTY_BYTES,
621+
deleverExchangeData: defaultAbiCoder.encode(
622+
["address[]"],
623+
[[setV2Setup.weth.address, setV2Setup.wbtc.address, setV2Setup.usdc.address]]
624+
),
625+
}
626+
);
627+
});
628+
629+
it("should return rebalance", async () => {
630+
const shouldRebalance = await subject();
631+
632+
expect(shouldRebalance).to.eq(ONE);
633+
});
634+
635+
describe("but Uniswap slippage would exceed bounds", async () => {
636+
beforeEach(async () => {
637+
await setUniswapPoolToPrice(
638+
uniswapSetup.router,
639+
uniswapSetup.wethWbtcPool,
640+
setV2Setup.weth,
641+
setV2Setup.wbtc,
642+
ether(0.01),
643+
owner.address
644+
);
645+
});
646+
647+
it("should return update oracle", async () => {
648+
const shouldRebalance = await subject();
649+
650+
expect(shouldRebalance).to.eq(FOUR);
651+
});
652+
});
653+
});
654+
655+
describe("when below min leverage ratio", async () => {
656+
beforeEach(async () => {
657+
await compoundSetup.priceOracle.setUnderlyingPrice(cEther.address, ether(1400));
658+
659+
await flexibleLeverageStrategyAdapter.setExecutionSettings(
660+
{
661+
unutilizedLeveragePercentage: execution.unutilizedLeveragePercentage,
662+
twapMaxTradeSize: execution.twapMaxTradeSize,
663+
twapCooldownPeriod: execution.twapCooldownPeriod,
664+
slippageTolerance: execution.slippageTolerance,
665+
exchangeName: "UniswapTradeAdapter",
666+
leverExchangeData: defaultAbiCoder.encode(["address[]"], [[setV2Setup.usdc.address, setV2Setup.wbtc.address, setV2Setup.weth.address]]),
667+
deleverExchangeData: EMPTY_BYTES,
668+
}
669+
);
670+
});
671+
672+
it("should return rebalance", async () => {
673+
const shouldRebalance = await subject();
674+
675+
expect(shouldRebalance).to.eq(ONE);
676+
});
677+
678+
describe("but Uniswap slippage would exceed bounds", async () => {
679+
beforeEach(async () => {
680+
await setUniswapPoolToPrice(
681+
uniswapSetup.router,
682+
uniswapSetup.wethWbtcPool,
683+
setV2Setup.weth,
684+
setV2Setup.wbtc,
685+
ether(0.06),
686+
owner.address
687+
);
688+
});
689+
690+
it("should return update oracle", async () => {
691+
const shouldRebalance = await subject();
692+
693+
expect(shouldRebalance).to.eq(FOUR);
694+
});
695+
});
696+
});
697+
});
584698
});
585699
});

0 commit comments

Comments
 (0)