From bc7822193a5ba001ecffa583f6c822b0bcbcfaa6 Mon Sep 17 00:00:00 2001 From: Richard Liang Date: Wed, 27 Oct 2021 11:12:10 -0700 Subject: [PATCH 1/6] Add Perp Leverage Strategy --- STIPS/STIP-007.md | 165 +++++++++++++++++++++++++++ assets/perpetualLeverageStrategy.png | Bin 0 -> 22236 bytes 2 files changed, 165 insertions(+) create mode 100644 STIPS/STIP-007.md create mode 100644 assets/perpetualLeverageStrategy.png diff --git a/STIPS/STIP-007.md b/STIPS/STIP-007.md new file mode 100644 index 0000000..3326e6b --- /dev/null +++ b/STIPS/STIP-007.md @@ -0,0 +1,165 @@ +# STIP-[xx] +*Using template v0.1* +## Abstract +Manager contracts to ensure decentralization, automation and safety of leverage tokens using perps + +## Motivation +We’re interested in investigating perpetual futures contracts as a vehicle for designing: +- Leverage products for a wider range of base tokens +- Leverage products with larger leverage multiples (ex: BTC-FLI3X) +- Inverse leverage products +- Basis trading products, e.g for products that buy assets in the spot market and sell futures contracts for them, capturing the difference in price between these as revenue. + +Similar to our leverage tokens that use lending protocols, we want to create manager contracts that ensure safety, decentralization and automated rebalances for the backend. + +## Background Information +Previously we created Extension contracts for rebalancing the CompoundLeverageModule and AaveLeverageModule +- ITIP: AaveLeverageStrategyExtension +- ITIP: FlexibleLeverageStrategyExtension +- Implementation: AaveLeverageStrategyExtension +Implementation: FlexibleLeverageStrategyExtension + +Below are the key interfaces for the existing strategy extension contracts: +- function rebalance(string exchangeName) external +- function iterateRebalance(string exchangeName) external +- function ripcord(string exchangeName) external +- function shouldRebalance() external view returns (string[], ShouldRebalance[]) + +### Comparison between the ALM/CLM Leverage Token Extensions and Perpetual Token Extension +In general, external perpetual protocols simplify the lever and delever flows for rebalances, and many of the previous extension logic can be reused in the perpetuals extension. + +| ALM/CLM Strategy Extension Feature | Perp Strategy Extension Feature | +|----------------------------------- |-------------------------------- | +|Methodology - Flexible leverage is a superset of most of the leverage token methodologies. Supports the fixed ratio (FTX), and Binance methodologies|Methodology - Can use the same methodology as FLI or another by toggling parameters on the smart contract| +|Rebalance execution - Use TWAP and oracle to limit slippage|Rebalance execution - Similarly, we need TWAP and oracle to limit slippage on rebalances. Need to keep track of what the virtual addresses and spot token addresses are.| +|Exchanges - Ability to use any DEX to rebalance by passing in the exchange adapter name. Each exchange has its own TWAP trade sizes|Exchanges - There is only one exchange, so only need to parameterize for that| +|Safety - Ripcord functionality that rewards the caller with 1 ETH if above a certain leverage ratio|Safety - We can use a similar mechanism to add additional safety. The benefit of perp positions is that margin requirements are much lower| +|Leverage ratio calculation - Take the total collateral value / SetToken value. Valuation is calculated using Chainlink oracles|Leverage ratio calculation - Since there is only 1 USDC external position, the value must be derived from reading the perp exchange’s view functions. Perp V2 uses Chainlink oracles| + +### Perp Module Interface +This is the current iteration of the interface we are thinking of for trading on the perp module: +```solidity +function trade( + ISetToken _setToken, + address _baseToken, + address _quoteToken, + int256 _quantity, // (10**18) + int256 _minReceiveQuantity, // (10**18) + bytes memory _data +) +``` + +## Open Questions +- [ ] Can we keep the same interface for rebalancing? +- [ ] Should we keep the ripcord / safety mechanism the same? +- [ ] What functions are needed to derive the state from Perp? +- [ ] Can this be generalized to use FST and PERP? +- [ ] Should we update the contract to take in a generalized methodology? + +## Feasibility Analysis +#### Option 1: PerpetualLeverageStrategyExtension +- Similar to Aave FLI extension, we encode the methodology in the contract +- Allow keepers to rebalance the perp leverage token via the native exchange +- No need for an external DEX +- No need for a viewer contract +- Use Chainlink oracles to price slippage and determine trade size + +#### Option 2: Methodology is stored in a separate viewer contract +- More generalized version of the above +- Strategy contract reads the methodology contract to determine if +- Allows better upgradeability +- Significant refactor with unknown upside + +#### Recommendation +Go with Option 1 which uses the existing strategy extension structure. We can always create new extension contracts and upgrade via that way (i.e. we upgraded FLI 2 times) + +## Timeline +- Spec + review: 2 days +- Implementation: 4-5 days (comprehensive integration tests will be necessary) +- Internal review: 2 days +- Deployment scripts: 1 day +- Deploy to testnet: 1 day +- Testing: 3-5 days +- Write docs: 1-2 days + +## Checkpoint 1 +Before more in depth design of the contract flows lets make sure that all the work done to this point has been exhaustive. It should be clear what we're doing, why, and for who. All necessary information on external protocols should be gathered and potential solutions considered. At this point we should be in alignment with product on the non-technical requirements for this feature. It is up to the reviewer to determine whether we move onto the next step. + +**Reviewer**: + +## Proposed Architecture Changes +### PerpetualLeverageStrategyExtension +- Remove the “FLIViewer” Contract from architecture +- Remove need to pass in a DEX to trade +- Use Chainlink to retrieve oracle prices to price slippage +- Use PerpModule view function instead to calculate account leverage + +![flow diagram](../assets/perpetualLeverageStrategy.png) + +## Requirements +- Same interfaces as original FLI strategy extension (do not pass in an exchangeName) +- Supports the FLI methodology, fixed leverage ratio methodology, and Binance flexible methodology +- Same mechanism to limit trade slippage using max trade sizes and slippage limits + +## User Flows +#### Rebalance +A keeper wants to rebalance the Perp token which last rebalanced a day ago so calls shouldRebalance on the strategy contract directly +- The keeper calls rebalance on the PerpStrategyExtension +- The exchange's max trade size is grabbed from storage +- The current leverage ratio is grabbed from the Perp Module +- Validate leverage ratio isn't above ripcord and that enough time has elapsed since lastTradeTimestamp (unless outside bounds) +- Validate not in TWAP +- Calculate new leverage ratio +- Calculate the total rebalance size and the chunk rebalance size, the chunk rebalance size is less than total rebalance size +- Use Chainlink oracles +- Read max leverage parameter from perp exchange and ensure that we are below it +- Create calldata for invoking trade on PerpModule +- Log the last trade timestamp +- Set the twapLeverageRatio so that we don't have to recalculate the target leverage ratio again for this TWAP + +## Checkpoint 2 +Before we spec out the contract(s) in depth we want to make sure that we are aligned on all the technical requirements and flows for contract interaction. Again the who, what, when, why should be clearly illuminated for each flow. It is up to the reviewer to determine whether we move onto the next step. + +**Reviewer**: + +Reviewer: [] +## Specification +### [Contract Name] +#### Inheritance +- List inherited contracts +#### Structs +| Type | Name | Description | +|------ |------ |------------- | +|address|manager|Address of the manager| +|uint256|iterations|Number of times manager has called contract| +#### Constants +| Type | Name | Description | Value | +|------ |------ |------------- |------- | +|uint256|ONE | The number one| 1 | +#### Public Variables +| Type | Name | Description | +|------ |------ |------------- | +|uint256|hodlers|Number of holders of this token| +#### Functions +| Name | Caller | Description | +|------ |------ |------------- | +|startRebalance|Manager|Set rebalance parameters| +|rebalance|Trader|Rebalance SetToken| +|ripcord|EOA|Recenter leverage ratio| +#### Modifiers +> onlyManager(SetToken _setToken) +#### Functions +> issue(SetToken _setToken, uint256 quantity) external +- Pseudo code +## Checkpoint 3 +Before we move onto the implementation phase we want to make sure that we are aligned on the spec. All contracts should be specced out, their state and external function signatures should be defined. For more complex contracts, internal function definition is preferred in order to align on proper abstractions. Reviewer should take care to make sure that all stake holders (product, app engineering) have their needs met in this stage. + +**Reviewer**: + +## Implementation +[Link to implementation PR]() +## Documentation +[Link to Documentation on feature]() +## Deployment +[Link to Deployment script PR]() +[Link to Deploy outputs PR]() diff --git a/assets/perpetualLeverageStrategy.png b/assets/perpetualLeverageStrategy.png new file mode 100644 index 0000000000000000000000000000000000000000..e43176e56aaa4cc4c4750f13afec59ff924633b9 GIT binary patch literal 22236 zcmeFZc{tQ<|36GBT@~7pT~~{WQrTxPm5_ua`<5(YCu7L2O-U#!>x}HX!Pv%HNfI+- zoxxzNGZq7nfPK-mM!Cp4csn@_y)lAogR)0SXOp3CRw; zcjU;?BM&@J2p-i=y<>brQ1H;RH?Ip`XFY7+^Q_6?>$80!+zp>!H-)W@85e2TBOgXh z>=%SX0}?hkxFCMdyan$utij?m<=lxqz`OqcpZ|B9KyzO|t}H-@lyVfuhmUV}#1C=8 z)02`|ma6H@?52@Qj;%zj4untln^fCmMLyk`HLOO)Wd_t#n7n_Y%k}ecEPbUC_5mRh zAQ|@~>(D^+j1CN<=S_Z8F-UbMFRU?AMs6=YKQ*kv^%ae~nlVbaC5 zgFzLkEB~fAGxMN-l>%YWM%|tBb=%^B%}Q7r)+(&mhoPBal&tPOV_IwHGV04xB4o!% z`7ZbB;;ntcR^O~n^2Vg2q=I(BY4sR$D|A`QI?;G4Zv9ca-6+I$uxe%XbEgb#UU=h>6!GZ#Z-wheCF_!_&rot+7B=8zZBaq%iSFRgZ?gfrf3Z1xkySyR$+{6Z6x{{W`sB*83OV%^q*7U0`Tjt)iIR=DPi|ETF71 zFpB))gHS{@jDZ+~aC*^ZnyfVwr1E^rEjlR&ZB$dksGti}Sd1C1u{J*!6g<51dX4Wo z#W!wanaT7qk;x2b8rJ9KpW}oT#cdd>J8lVBdk!p}W`~qk#cXJ=Ja@|Jsina?V9?9jctqXC^F3c6IT9nc z_{0iK8z@vq!;`B6Tf&mRtF#K=kecbR@ptxzVh%=BMnok!Rt)a!9^^o_mz}zgl{%6Y z)~S4{8M2z57=em&Yz}|Ew*IG3r?rDri00ABWO>@k`STYo9$4AchkifWYOb-qppKWi zQ_q~2k+%@G3qlUK(JnPthy3jH^Me_pBXHN4yu6VuJ<8(zWEaNBr_~`C%ZQ?Rt-Wt7 z2kgzRv1NHh;N*YWkCwZ&&h4&at|vRE%IUGC_RyQ>x)Lj}AL2~g$73i7a4qNJvDmGL zoVSTLtD>LQc-(OKst>0TEd$oxUTA6A+0aKq-rRltAyiV4_`HJU_p?)_wnSYTLZ+QIISh(v`SC zKK*Ft5m%+%rG}};TH!Z#1RRYtH(w<|a9we)M5=wBvq<_9?=!4@hUXKG(8VH)dYW?@ zr(@4yMNuoHtM)BlL*&r$tWVVG>AMGd&TU#%G{MqLEo|!ie!WbLP>G=@xqf=-)!^Oq zP4mlo_YR^)B|?wOvM>s9^!;t8x$>i0E!chq$q3^F|Jua=y0S-M2wc&pXC*Bt9=}>882)y+7PRWlsc~x{;1zxB8a{0t z;`x#h{UbX>SV(Z^itp_f`#t_$tPnTLx#cJ7;ab3(>|YBD3ESDXI5d4!A*+x~?zQML z4H<#IHS6Cot=WO2gsmCwy& z@tEt%cMn2YdSChD=TgsuyJ{Ivwak7zsz3pm%(+TPyG@(1Hs1$ZO<)NJ9~8;43GX{= zW~?_+w6zs#E0&dclIz)}gd}0icTH8!MA^RDpy3zN+H`Ae=M5ig?>;@1-&tWi<(il7 zbm~JEMo~ohuMkSwM#v50uW!;v(w9yRfA2aL8^wyFwwtF1EZ!p9(Uy#ugE0cuUMfE5 zqMWMft096P(rA0HKkScfpOW_`m3FdB!?H{6#tzr=BS_Y1Rc;%LdDh;)Bwlcz`S9^0 z-NfQS-LNKwStW5JcKB9<##ZKM>7CoJ9hPI5V{gY<&uIHYU?3HkphH78DXjl0O@7n;!b4K7HYV^31wP`;L3y&E^_&Jz1Cp_wQ2Wr6&t% zdeDq@GzJSbF;u0i76Yjg0w)!clqm=Iywh+WC`|9EbIZ=C+)nlxxzJu#13qQ{LX}c1 zG%=YlnPycz>NN1J7Ww;xj2H}e)+8c+rrlo`TiW$;-3!)Rm@LBemO)eF=eh9kg9j09h;TZJT^aMFqfAs_rQ4 zP1f$9%uZr0)N4lUv=LbN{x2$4?Nb%bNn=-9(XmiifAbLH9l3sWq2?9l8`oa*(&1`~ zchl%62&}8t`*Ool0$EY^5pc{a=PzCcp$78ON-(}Rs#^YFl5|1)1E|FpjlV~XghZPh>*?^$aTH#J8+n5p}d47F4 zcRh0M8O)aX^(xt(y5g2!7Dv&B{PDOeNp!;Z3F=p>pk>6@jVPCubz_L|386+WRQ)5E z?Vg{iYG<9>@P|d31I_Gxn=^Y}&aH|Otj9vplo2I~1JVZ7#T_!L6jJ$H(ZKa(AiRiL z;!IPF(9#pnHU!gWwZwm!ItMPmzX9U37;}x=>C@!ow+1OP_ zJeniTQZc_6`L-vwuH?z;ae2t(P*g{TE^LzK@^ZK2`N3Z9`CXd4=!-N^5h*E}O-30u zf$AllAh6s?`}5~t+mnj%xZ*BB17$fTGTCEp;eFFZ6=mY3NOyB9pi!Zh4E1S{E^n}B z7rzL8oN1{h7llc|+V=d;NUfHot}GI1!Z2$YF(C=2cD6D0ZQE6Hgkrh(Hc0kUV@o$W zp(Bd=0ex=E>Yl~#s3y(Rl!N>4gMk}|^f8d!8`LWE@4BddHnx_z3GM`JN2 zi`%Jr`OUH{&|9>37hil6xAAAsPu8ipjh%|MecJnEg_>4#pui(D9C-O`ZSTE{h{aTH zpFb9T8O^D94Kc~q;j=xTRDIFi>@C^7;ZllU?ZMq*P+|plpJQBfFJs6p8MtbYM3;24 zjHx`|JO`TVa$EstFirc)W>hO4i@u`Q4tbv;Wq)nvS>H)v+qR$`1hgb8;hX8&#jz0^ zs;1MW4Og`#w+9gPXUm*jPZ|g_8l4%^>4?y0A#Edk#edBHMex`jGjcsyM8@`do9HglTO zZeOA!vNlupVTGz`1Y`|SlPASYeP0Ua8_%av47B~ay`X*%yY786n%qp z>ET;Nu6F)NOmBtc(vz*1Zb^t%oSJseKGaKXoaK=gqaMmXPd~N~#9jj0MGexq!qmgV zK6tX2qT7{DndFT*rQR|`{TljV_8MBp(}%v_szE4as{ z8SgKJ*ok)WO^?TJOVyNLED<9f!>!{3csW<)0@Hj#N5jfo$M4<~lz6l>WVpDY%Z9^E z9m2i-dw+X0c3YPO$L4;@yKH`|_dsRqve~$OET$c)$;?tn-gl}kXmhpQANFlv?IKFi zWBfF7fBN4?D|h75g-dqItoLe2uY?C)Ya`GvKBDt3Lek^vB#|YfT^rpnzi(B!z93?v zPLeMi2G%Yw@8kTF+#Y&ipr$TomQ94@%cRF07Vl+Hx3&iLT#kl*?~6SC*S8J)&#huPOnu;<1W8I%Uy6e882Q>O(F0ULewPYC{Jb0)C3-)#y_2cj%shb+4 z0D2`zoL@lU*Ab_GOf20QL$&q&UBDA@t=2kW*A5WK4g}4A;N`dXuG49}83?MS+Zm;v zr?-Q&F;Na)kAzxf)cI8ZJ_l}dA$&ZiaeWl3PI?5I3b;NJGoJBe<0x%i&#| zf@i@lH!iCemsBq7Lm%O${M$l&<-Y33+7CdX(WZy!xm8@&VbQMI|5_NX3QCmk64be$ zw#`4ps;zuesepa7{vw}SW+!?cDx~=HqKA3hSgsM?J!2HV5P9CH2}C~l3RaQ?2_M@C z9y<;z1wzgXK9-j_5Ya73Xl$o~I_W|ys`u@)FG4}-iFX{8@1=grhWtwtr}Lz z1M~HLYi96_na8_r`|RO(O((ZUA5Iti#~w8vyO#i!TFZpt1=#rKk7^jT{N%Qu?rLgk zDoFL27au9V{w%~|S=gEd{-016jnHrZnA>Spv_FKI;32=_`Ft*p#4Q3|a`l+kPDJ3o zt!dO$>xc(MGS^`~5a*8{lPsZjq4l|9XWch+E~SCvC9VeK3_!=d4XN5FS=4#-ZmR=b zH^UAqm8KBwJxABZ4b0evX&#F~cve~5VF}IId{@QbRLl6tWl!jLi1BvUv;C&w;XCw1 zoMz!LwJRYl) z1nWxLDcF{myWH<-5w1?rkLCwzn0>13n;aR9U^qujlutnEf3EZMFD!Pna9fCe?a^BC zD&N=Md$~WbqSin!)!7@mb-G>LbHL*sUX7UJ;CC)XFfHmy#Ia zA^QA~`@-UO;Py`&y)znW6ID|s`(x+HkA&KI)%ohkmFwG#QnqmOoF$U)Z(B^|#a`Cf z8r0Gt*}hYReytmK=bs1LfLBK0ZWPLFG z^P{LFc>j{_nK4=+QOLr4G&qGCVs2jV*)l_1V=S}_SngcF2k88rAFs!b!e+V3I0boc zAzllNef##A18w1*3E%anF=eT1*_ede!C6;HAC+Xco=CjT=SICbA(&JL-XjC2sZ>Tu z_%vE_}#|p$a zemzD@w?u1nDqM{yA(!0Cw?nYXnzj$(Ys@9CqPUIyZv;6QXDV@C@|MP+ka+@UN(v#4gw}3Ut*wWvBb9LT6x{kYV)h zmMcbp{$031=&Z}*Yw5e&F5tNqGUkZmWP~ulw5X7ba(7~>4fnX8SC_W=U&TpI9IlujLl?u_(k-J| z{hW}6UIW%xIRNpzT^-KXS0~rjFOPo<@fRMK$9~)U4hlZ~cMgp7$1*O)Z4}9aC}>h^ z#?2itrP$Aad1$#s+5Cu}k5!F#X5qzJ#hAnib-pL2XD~dIF8DBk%Ke8$7PlgHVx%nC z62Tv;+Xy7fRsryE@YaA)`b^SjC<^{dEy*Hep*p^r@in_d)*C}keM0ch*D5!tMU*_HdCC@dQiV*thLv?({h!5X(x3AYM>qxH4w-M zD-F{XpHnx7>ITe@;^#-4d8ykOq1!MI+;elR##=;wnA!y>J=Ycu%{vjA2uE{c;U&DH>`AH2?*=Oa=wpZ>SUL#FJp4~)>8}D>vO-K)!b;xI53Ik$YcgOS3)4@1!dac zkO(OE`TDbOH>nFhER$?#{+d}m>K@-j`bw;&?3~~u?$)$&M??QH69&q`+}evc3fjz3 zC`nrkxwy0~)VTG&&+Xn~=z=!2OF#U^Q>FQJJwl2?Sw)`hWAs3Q8;n*&lB)EsqdumF z4IJ&I1+%0+JVA`1vs}Drjh){9xXIZd-B<{ALo$RBSYC}7z0gC`&A2jjkV*kb#CtHF z^sAAk0#y4m=P_(9JgDf+ZWeofUYwsrt6}oDq>{w-d_Y|Z(|*{)aYYW_8vif8)zW+s(G72hg8 zek_J+eaWh}dNFwGEVW|#l*N3c|Ln-wB~Tc?A>+x=Mhou8VM)#2IsaK2@FB=CTR~+J zUlb zVSRH#c&z%*_XW%d#8Kl{Kf)GLYMcF|yfG3&Xr5j){>yiml*P&?g{?b?96}nr9OQ{H zm}H*|ysp@I2kM;=VF*{0v|L8Qezb-`w8vM_>;wth~;efaV)AYe7tWr#(mt_6Q4-rVR z?5i9V;^^a#lxj*9H}q)cMf!Do-HPyHU-Wp$i?+?TKLNur=}d zu9BH%L#Vz#BHAv0!hYTc10}X z!cEOT@N5bbfP(DMJXz@DK#!zw0`1*M!nkC!FE@bW=f3&z&Gpj!jf3#Ei_fIAF{&L{5 zL+x_<*ciI9ej2eFM&nTlwoAN}0^@j=X1x5{%}wEP;(>1(=%60B59m-@s01kjOOMxN zGzKmoF;j6rv}&=OGRGHvZt0}ToF{&Hx~p;GawH$~%*S)=LGTL|M#QE!{+FI^7yoOJR9kKv0S-g0sH@Bdtz+0Fz019fu1%-c zkcQ_KyOnxD$@4o1%G))ph|O;Yk9+K1Q@kG5LymdgJqIR{2xf$YTqYHPI_|C|GvcQhr>u9VZma zMuhL2p*`!w4|MG%i1Zn*Zj}_qE!x=Jv-s?GQQ>`*q0O;n$?+i3-*-Pg)i6hkz zUhx|HM?=89&dS=GG*o2WVhALS0I%QSA!;n=3%E`Fp9T9`HofinnAAY;`yb6|nqgmd=3SfBTSaJmp zx|tWZmxZW8-A1W%ttZ2}vlL1|j#)$NdG;Xd0HbU zv$dpjAz-mQTj24qN|RxGaISMM;fAhwInD#i{}UiG%cBlUO*h&luGh6TCV-{gVF3Pd zee$I5nk`Y4Wp*DNL|9Qc#>i!#E2IxpGBn8s7nwxud!R6u|5B@}N&y zy8m~ePBAkf1OO>QNlE)aC~+1N(Iyf*HsI3 z$fEz1#h^)k>I1u`{cwa`?ass1a40@`Qk<&*oym6{)gy%*o9c(1GwT#d7KT{l1T9A> z(-KehpXa#fLdfc1zR?*M9;9B!H2-Uyuj_0wF3a_HN}eFWk59Hw=549+Xec6P+T=ZC zUi8c&BPT6b^HF}T&@gGhNPof^<8X`=@2t~YP8x$ z!GG>>G_p9rK^at9SLc{u4xL{brMGBBfJVLLVtd3X=Y z$ACV0A1wTe5IVUqgewBkXpwbV#!3>HVU5w4+s9$Ek#-84xrK71xy6G8{{!dlV!j0b z#?+Q3^j>Q?J~EKfYXg0xG}mV06Z(EOvH+c+epnw08*u1h6!O20`s9B92>gEk(H_{q zjUJfN2)qvq20>pNscwhLs;DH)M$S^ZhaR!QR;$mkZZAfDB9K-VPXzqF zzc|5}U7FtBAE)FtHYie=vK8JTaRjam+_+{M?OHun9dHy071*#%Ezfd)F|(9%Db>`k2YI>A8YO?Fhr50VP z?MKmEb8#T)`6sT_ApkzXNqv#ckTX;d2SBdU(M`s@zxWBn%z3$s{sASiX-YM?#`Tr& zj=^Q1qrr%Pu0`9If^`4Y!q1up3m7&onm8+PA3tDwVRiVZZW(FH5c+_J{dt1bJ*;#3 zc(#J#rg_sE_h!pbWFj9vE3gHwiRs_B($QIc+HmK@RZ~l&u;<6%*H;(^!8{Ak$H;vH zKjyyJXDrNp85@1--Cv36oAU)r4{tO;Z?1g(*=BA8i$czO(2uPCkyUoYKO0SbBh7wt-dEuxig`jpeOv9IrfRmi!KA^w(fuxi@1b634<0l=8nG)`DxU zOuS)>OXwGwOotmaC52zs$m9R{;SkaP!G|^LDj4V@1%L13##FKq-C?cL{L8ZcN)%BT ze!ai1KR*%`R{wn?n?(KcHM%`eYS3B*<+r1nkd1V-vqSbx8Q3lIhxmd7^?1cWGkF!S zhecF8N4_w_=M88T$MGi_8zOyz5*D+GR%lI;Npmo2Fh$%-VOp9KKl3lF%z8ZXUb`0Y z_=bMidhfTVApu%{|m``5e z=m-H=KX*s$v=xAk&xV%MHOmCD2FA`sKx(}RoPp+g`~VT{V#L}yY$`%e{Iln^X5&x0 zHGXE_gLvL8bh2<}@#$mn?Z?1v6Q^HUK4+1^OLc}59_OF#pb)G9JrfW2+XDcx2j4;Q z{{+Mzch&R8N_LX{Ki$gb8J!uvt+=J1Pw(e`47DqWoBgcqIDp-BI#Ao{u&g-#H1!K~a zfzqV2l6@ja!tUf79j`{ilRRdNwmrZSM{Lu>ek(O2LV-OlfI*mVDrUJt&*2Nd1$HziVdtz^%yE*7v0G;t4C@< zVJ^~h#JvxmcZcYUf}}}Q6lIY+1?d%-en4?Ul~h13g;EypvlB>Bs7bmkjjhC$?yH-{Q)G z%d6HckYEq1Hr`YzRH3-ZL28}jzS|`LAU?<>^YQcf>jfs6M*Ee%UkCdmj`rlg3`S}? zcvjDy!+9#a!F{NUNC!N7lYh0Y9pP8POjh0-Rw~i7v%VTWT+yI0KED__+n?=1yZSfk z1@dbv6-drcMohO&BB|$eGgc&2=H&3pMXy~*seU~lkuxdj5Q6pi5WT~^P!do30c1PQ zaCA`^4BuP_7-zgdSx>6YbqgJk)8pD7gv$UDNw}NPr6x14?7USzY81lgIQ)J6kGmcR z3N_pMWWq8bAnAIf0Wr-LqxrsnAF|%pgn?M(f8E^TVyj3yer4B|%s`1F*f04ZXKh2; zvvJ%Fpgo}Hne7DNN;z}_sK?6gx08l<8<#TezMYNOOmPw*Ovh{;D=(<@GZPqGa_ITekiJAb)dqT1+>nhwVfZosvQjmAW zxO}~%nVqmWd-LKb>WiFV6jO10?lnmIWA*%#PaNA3LpluYdf0*Iw~FXGkBd5Ft?9J8 zf<6;@rRF{B;)^f!FQrKKb%(H24T#h3m8Ys>I~e(fZ*abK_B0T=Ib`**9m)gT-_h-d zIBUk9)ijbaE4~EeI#mw(y1x|BoTF3AD?b zgEng6Kts0pz$ZMK|FS!=FqbB`1u&xdry~I0fAB8f-WS7+81>O&&DXgsp2qEayScYp z;@W3$BF%70)Vd_*f>P+0un2k;T;V2Ny&C4kAct&KLc+Pj)|Aoov8bv18^L=iJ@(MpJANCz&jL z^S_ZIJ4;QOjpG%cwS}u$!`b-?hxJ=giak2ZGA~mSvVTaFO_(cS{{nwIr@Of=Rd%%n zT&Y1L9gdm`wMGDx&dwocSIjE8Jg&qVlURe^6}w=$Lg;iknaOmN0dQdeELWE>&X+zG zU8Vqj<2QI!<{UB`Eq#gPN$K)HZ7f#H1s(WAsWjL9iqO;Pu@r=h0x*g19#)gTuz#Sa zK2{rbNM0$xQtRl5!bzZ%s^EI-sM8td!Y}8~SpjzV(=+eojKv z1Aui`O2`FcG|I12bJ&{E`jScyfE@k{%e>flnAO{M6g;x* zFY3@e{xaCJX=H?QJGievj|ag$=l0*^1Ue|jg^{+qNRZcnaU%Z)JsI^&2?x-xy*6rR znhLF3;=GrMzy6isen$CU-xL@kr?fN&jBet5WiB;LAlr`v{r#oFlt&5n3IHsP!`AGS z@MMYcA_4doca5*G7k(~vNV~NL=tc9&``?vLvj#>(&&p_$(qgbg`ZJGmIMaNa2U;FZ#y~@rcAYD_tmfu{9jb9Tp z;z7pEezEJjUM64Pi7M;4C({XnT^?Qo9(k#z)1~<0JxJNxrM| zK-s0KW1W|Y^=sLIoUb?!K*;3tX!zH(WqDNz$t^(H;2XHII^ zIT=bg8~GppONr5F2@Uh{&Of8k(|jHRL9Qx1#Qg9d3a1HoeZ1A1cvM$%-6K4F;V}BV z=J>ybJbXnXBO`tMAis4ZN==fR+U)NMLgd#lq9p;IPe0BamQq{0LPbg~I)?3Ebcuz) zOQ>PjE&(jMnN<1PtF&X+|52GlilQA+i#D(A42C$}4v;~mWGA7s*45P^jv2|a(K#}g ztgE}F9L@jh-Q{IswZi~qM|ZaJ4_wKbya^rr*i5jDn@q_1&S#RC9d}#LG8!gzgjsPH zpwD*l1Neqhwf~U*$Dr4%t4V@o+ogGmq=Le-S2TCvjdP%P@=~^;5kDeso~)Yv^5?Nw zSGk8>a&H8li2DAE=9d$Sz7=jmej1vm&06j*m)+*97cBz)0Q#crv+V=a8e-u?;_y813NCdJy9U~=I2}p}dhXQRKz4|LRK=E$pN`6^= zBw!TqD~*>;Ow8$_^AI|d8Q`YKTlxff)R;afg{4nieO`}fg*cHIpOkB9-I9E(txbywBwU0u$N5DR=W zrUsqE@kmJllnRufuso-H+mJ>V)LOKQ#GbBy=tAG)fiyCk2i9=5uwyAP&el6{vrHi- zQYrNXtd-ofG655T7=jMCja2pA4?!@Tgud$jqnfgOm=6qJIBi`ik-nuu0{o*W*5i@T zf9b!@h{o=`n^SS)yE9KfF}44amZ?z3jd`0K%2e-H_r$tRE)9ZDMF<%D<2^1pz+}zIst5_mOMvBS-C%cV%lfkC`PI0B@H1t`!gDIim8h z=+Nz6T@Om8OraituQl^+aBiOY=deDVtk+BXWS>SlQ&{iHIU1|e@`rPK6C~jufeiXO z2~f{Vw1*aA0r?Wm?shnYq8~%z2XA(BQe@vfFi_=V7*5$_kZDH(wfa0p#PX0qT%+b3&$K7wLkrw_3F#GdjD+16v z(ibFFh;I+wH2?)Hem2@Kpln_*5cKbGM*sXu2Yc;&#CRoeBM3V1P}>je47xLctKP)SB`vZ23AR6kJ`qMuBBmY6C;G_)^GIR zG#}iR;Jz&&eqQAD3E!xMwgDR4_l`PpF%)fvqWQIJLK5#HJF$Ft4X2d@-py9~dcw1A zv~Efb6~kX(klgFvUgu%cLcd?dn_h?Usy7y#HMvCmGEwuX9 zNLZ?%OG=ZWSgNduNDTk&-%4+9orbuw6>BY*@qSx574|P%C-QIJRrq7wG!_`VQaElX zRogYv+yUuqjal4QN*5!%76zoa&kX=MCRnOzRTs7|k+0Ju<8p0gL1dNi{J+Dul z0|bT_uM_~9%uXsf=m$EhgADBs4+-~Rq%*FWU%VzM@lNi2T3QIvj?M36RuXZPE_~-s zj1n&B9mbA(z4tSq8D^+b*nx8mff)iG{Q#woxxd7)?azd<;{o;c4yX@>8E;CswX?saoRRp=&xubYQtbKVTo&-pXITFhJ*}W;W5XVnVB#jR0A*@@Q3|po&?*P-Zgih+QS-y zU*3wteyad25B28VtAFf`&AVtHx?b_hPvhr)%2z03<1A3r&R+x~`kuV_9}%h!%j-T^ zK_ddz1cf7(?;V9saKX-gQ<;r)%HY{`lAbjW?>?u|bXDgq*09tdtptfV#=ZBZM#J&t zr3+BQ@vh(G=|XdReBI<>ciV#Y`8(Dtd_jol;M8R;A0!%OGm9Jm2sj zIK#)qE@H_YePjr^tK@Va%4U z0kyH1Uar0O#Tt%R`V$uB4jU zrCY{R-K+$Swt71#F2UOeoZv1Nq4sx5P=y<8U1A{pK+^A)>aS>6L)-5+G*i2|A=5K6O-Co6!K6t8 zG3|~%bPZ3u=}>T2{!rOY-FUiV^Wl3=jC3K-;WBKkQRCL@e2W`#i(aIMAC_}D$+qC_R*>WETh3ZLx5A) zA>AG-Qlt-5i%=k1FA2;az9s+(%oqUX&E)Bj?+rIc=+}PEMt*saM{jnR+MiJ@afK8m zeT%;KZXyKrw%sw+r8Wk$hf7*7Y3tb*YEA87cl=WginTuj1*mbibAUt^2pF;R$=RT# zM(ba{IzQQlu2zw;k2aNd6EEmV{|(Ug+VgF2+_ea|z5EhKwo;6Ncw-1?E=T(GUrzRV zY42D|Gz-!m+MVJm-Ufe4Zc*ea(baqWxJLp|Rhkv?T_p*&=ytnzEyI9euv!cT1EZ6RGdE)Xd^a08j#51Qs;_Y_awyxRkFGvnI0yKweF47XjJ4ZE)*~VB1-3`+)-E zsTv?qn?H(0Prr?8TmHn7>JOZGeu%Mj0+a~r(;#k-l{u2|r+a?#t}%N1wq}mF`d=?U z60$;=G6An(9$?Q$5z|FY!4xYEXjDCF|Pt-klWq z*Pc@WSrqvfU$S|g@#U^f9BDYcoVobfyH-j>zbcwMh4Qcga@Z&A4Q&v%e3H&av3nB;r7Zf4b@fU*!&o1 z0L^N{5%NGg<+JNqqVu-==P${JsS0(njWd4O`i*K6^rdnGg1#*^cG zM$}Ry`UR>sq71Rne~P@N)}d+zcb_ih^B;{viUJ1I zNDj5=8NuB6K?pJONX8=fZFy=ks?udn=awEg8O$ z`Dxg=I)5ghK{R^W^)`2(?w+4}or5-lPRqKRHI^tB0W_C+0rt+!Rr1a&es=SajzP

pOB^ThNnyx7`ACJHjnhptJ7Oie!jSRr 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 k-}*Mdu+Yx#Ee@Fc?ApigX literal 0 HcmV?d00001 From 5f3895980120e79b0f95d411d6d7619342620ab1 Mon Sep 17 00:00:00 2001 From: Richard Liang Date: Wed, 27 Oct 2021 12:04:46 -0700 Subject: [PATCH 2/6] Update perp tokens --- STIPS/STIP-007.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/STIPS/STIP-007.md b/STIPS/STIP-007.md index 3326e6b..72125c3 100644 --- a/STIPS/STIP-007.md +++ b/STIPS/STIP-007.md @@ -1,4 +1,4 @@ -# STIP-[xx] +# STIP-007 *Using template v0.1* ## Abstract Manager contracts to ensure decentralization, automation and safety of leverage tokens using perps From 3d279c1df983bd43b1f287323500cbcb04e45dee Mon Sep 17 00:00:00 2001 From: Richard Liang Date: Mon, 1 Nov 2021 12:51:07 -0700 Subject: [PATCH 3/6] Add checkpoint 2 --- STIPS/STIP-007.md | 65 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 46 insertions(+), 19 deletions(-) diff --git a/STIPS/STIP-007.md b/STIPS/STIP-007.md index 72125c3..58deaf9 100644 --- a/STIPS/STIP-007.md +++ b/STIPS/STIP-007.md @@ -14,10 +14,10 @@ Similar to our leverage tokens that use lending protocols, we want to create man ## Background Information Previously we created Extension contracts for rebalancing the CompoundLeverageModule and AaveLeverageModule -- ITIP: AaveLeverageStrategyExtension -- ITIP: FlexibleLeverageStrategyExtension -- Implementation: AaveLeverageStrategyExtension -Implementation: FlexibleLeverageStrategyExtension +- ITIP: [AaveLeverageStrategyExtension](https://github.com/SetProtocol/ITIPS/pull/4) +- ITIP: [FlexibleLeverageStrategyExtension](https://github.com/SetProtocol/ITIPS/pull/1) +- Implementation: [AaveLeverageStrategyExtension](https://github.com/SetProtocol/index-coop-smart-contracts/pull/72) +- Implementation: [FlexibleLeverageStrategyExtension](https://github.com/SetProtocol/index-coop-smart-contracts/pull/59) Below are the key interfaces for the existing strategy extension contracts: - function rebalance(string exchangeName) external @@ -31,10 +31,12 @@ In general, external perpetual protocols simplify the lever and delever flows fo | ALM/CLM Strategy Extension Feature | Perp Strategy Extension Feature | |----------------------------------- |-------------------------------- | |Methodology - Flexible leverage is a superset of most of the leverage token methodologies. Supports the fixed ratio (FTX), and Binance methodologies|Methodology - Can use the same methodology as FLI or another by toggling parameters on the smart contract| -|Rebalance execution - Use TWAP and oracle to limit slippage|Rebalance execution - Similarly, we need TWAP and oracle to limit slippage on rebalances. Need to keep track of what the virtual addresses and spot token addresses are.| +|Rebalance execution - Use TWAP and oracle to limit slippage|Rebalance execution - Similarly, we need TWAP and oracle to limit slippage on rebalances. Need to keep track of what the virtual addresses and spot token addresses are. We need to make an assumption that prices of perp and spot are close| |Exchanges - Ability to use any DEX to rebalance by passing in the exchange adapter name. Each exchange has its own TWAP trade sizes|Exchanges - There is only one exchange, so only need to parameterize for that| |Safety - Ripcord functionality that rewards the caller with 1 ETH if above a certain leverage ratio|Safety - We can use a similar mechanism to add additional safety. The benefit of perp positions is that margin requirements are much lower| |Leverage ratio calculation - Take the total collateral value / SetToken value. Valuation is calculated using Chainlink oracles|Leverage ratio calculation - Since there is only 1 USDC external position, the value must be derived from reading the perp exchange’s view functions. Perp V2 uses Chainlink oracles| +|Max borrow - Retrieve the collateralFactorMantissa in Compound and maxLtv and liquidationThreshold in Aave|Max "borrow" - call [getImRatio()](https://github.com/perpetual-protocol/perp-lushan/blob/c6b6a3810bdb37534d6931b8be24c5de0bbd122c/contracts/ClearingHouseConfig.sol#L91-L96) and (1 - imRatio) to get the "maxLtv" and call [getMmRatio()](https://github.com/perpetual-protocol/perp-lushan/blob/c6b6a3810bdb37534d6931b8be24c5de0bbd122c/contracts/ClearingHouseConfig.sol#L91-L96) and (1 - mmRatio) to get the liquidation threshold | +|Collateral value - Read the balance of collateral c or aToken and use Chainlink to price|Collateral value - call [getTotalAbsPositionValue](https://github.com/perpetual-protocol/perp-lushan/blob/main/contracts/AccountBalance.sol#L299)| ### Perp Module Interface This is the current iteration of the interface we are thinking of for trading on the perp module: @@ -51,27 +53,23 @@ function trade( ## Open Questions - [ ] Can we keep the same interface for rebalancing? + - Yes, this shouldn't be a problem. Remove the exchange name parameter - [ ] Should we keep the ripcord / safety mechanism the same? + - Yes. No good reason to change it and gas fees are much lower on L2 so incentive can be way lower - [ ] What functions are needed to derive the state from Perp? - [ ] Can this be generalized to use FST and PERP? -- [ ] Should we update the contract to take in a generalized methodology? +- [ ] Can we make an assumption that spot and perp prices are going to be very close? ## Feasibility Analysis -#### Option 1: PerpetualLeverageStrategyExtension +#### Solution - Similar to Aave FLI extension, we encode the methodology in the contract - Allow keepers to rebalance the perp leverage token via the native exchange - No need for an external DEX - No need for a viewer contract -- Use Chainlink oracles to price slippage and determine trade size +- Use Chainlink oracles to price slippage and determine trade size. We need to assume that perp and spot prices will be within a reasonable % +- Retrieve leverage and max borrow calculations from Perp directly vs using Chainlink oracles on our own -#### Option 2: Methodology is stored in a separate viewer contract -- More generalized version of the above -- Strategy contract reads the methodology contract to determine if -- Allows better upgradeability -- Significant refactor with unknown upside - -#### Recommendation -Go with Option 1 which uses the existing strategy extension structure. We can always create new extension contracts and upgrade via that way (i.e. we upgraded FLI 2 times) +Uses the existing strategy extension structure. We can always create new extension contracts and upgrade. ## Timeline - Spec + review: 2 days @@ -93,6 +91,9 @@ Before more in depth design of the contract flows lets make sure that all the wo - Remove need to pass in a DEX to trade - Use Chainlink to retrieve oracle prices to price slippage - Use PerpModule view function instead to calculate account leverage +- Updates data structures to reflect new contracts +- Update calculateMaxBorrow +- Update calculateCurrentLeverageRatio ![flow diagram](../assets/perpetualLeverageStrategy.png) @@ -104,12 +105,12 @@ Before more in depth design of the contract flows lets make sure that all the wo ## User Flows #### Rebalance A keeper wants to rebalance the Perp token which last rebalanced a day ago so calls shouldRebalance on the strategy contract directly -- The keeper calls rebalance on the PerpStrategyExtension +- The keeper calls `rebalance` on the PerpStrategyExtension - The exchange's max trade size is grabbed from storage -- The current leverage ratio is grabbed from the Perp Module +- The account leverage / max leverage is grabbed from the Perp Module's helper functions. This is used to calculate the current leverage ratio - Validate leverage ratio isn't above ripcord and that enough time has elapsed since lastTradeTimestamp (unless outside bounds) - Validate not in TWAP -- Calculate new leverage ratio +- Calculate new leverage ratio using Perp helpers - Calculate the total rebalance size and the chunk rebalance size, the chunk rebalance size is less than total rebalance size - Use Chainlink oracles - Read max leverage parameter from perp exchange and ensure that we are below it @@ -117,6 +118,32 @@ A keeper wants to rebalance the Perp token which last rebalanced a day ago so ca - Log the last trade timestamp - Set the twapLeverageRatio so that we don't have to recalculate the target leverage ratio again for this TWAP +### Iterate Flow +1. A keeper wants to rebalance the token which kicked off a TWAP rebalance one block ago so calls shouldRebalance which tells it to iterate +2. The keeper calls `iterateRebalance` +3. The max trade size and last trade timestamp are grabbed from storage +4. Validate leverage ratio isn't above ripcord and that enough time has elapsed since lastTradeTimestamp +5. Validate leverage ratio isn't above ripcord and that enough time has elapsed since last lastTradeTimestamp +6. Validate TWAP is underway +7. Calculate new leverage ratio using Perp helpers +8. Check that prices haven't moved making continuing rebalance unnecessary +9. Calculate the total rebalance size and the chunk rebalance size, the chunk rebalance size equals total rebalance size +10. Create calldata for invoking lever/delever +11. Log the last trade timestamp +12. Delete twapLeverageRatio since the TWAP is over + +### Ripcord Flow +1. A keeper wants to rebalance in the middle of a falling market and calls shouldRebalance which tells it to ripcord +2. The keeper calls `ripcord` +3. The incentivized max trade size and last trade timestamp are grabbed from storage +4. The current leverage ratio is calculated and all params are gathered for the rebalance including incentivized trade size +5. Validate that leverage ratio outside bounds and that incentivized cool down period has elapsed from lastTradeTimestamp +6. Calculate the notional amount of the chunk rebalance size +7. Create calldata for invoking delever on CLM +8. Log last trade timestamp +9. Delete twapLeverageRatio +10. Transfer ether to keeper + ## Checkpoint 2 Before we spec out the contract(s) in depth we want to make sure that we are aligned on all the technical requirements and flows for contract interaction. Again the who, what, when, why should be clearly illuminated for each flow. It is up to the reviewer to determine whether we move onto the next step. From 718509b87d8ccbfae50fa6de2f094103ad70f3fb Mon Sep 17 00:00:00 2001 From: Richard Liang Date: Mon, 1 Nov 2021 12:55:58 -0700 Subject: [PATCH 4/6] Update perp module interface --- STIPS/STIP-007.md | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/STIPS/STIP-007.md b/STIPS/STIP-007.md index 58deaf9..0904762 100644 --- a/STIPS/STIP-007.md +++ b/STIPS/STIP-007.md @@ -37,18 +37,14 @@ In general, external perpetual protocols simplify the lever and delever flows fo |Leverage ratio calculation - Take the total collateral value / SetToken value. Valuation is calculated using Chainlink oracles|Leverage ratio calculation - Since there is only 1 USDC external position, the value must be derived from reading the perp exchange’s view functions. Perp V2 uses Chainlink oracles| |Max borrow - Retrieve the collateralFactorMantissa in Compound and maxLtv and liquidationThreshold in Aave|Max "borrow" - call [getImRatio()](https://github.com/perpetual-protocol/perp-lushan/blob/c6b6a3810bdb37534d6931b8be24c5de0bbd122c/contracts/ClearingHouseConfig.sol#L91-L96) and (1 - imRatio) to get the "maxLtv" and call [getMmRatio()](https://github.com/perpetual-protocol/perp-lushan/blob/c6b6a3810bdb37534d6931b8be24c5de0bbd122c/contracts/ClearingHouseConfig.sol#L91-L96) and (1 - mmRatio) to get the liquidation threshold | |Collateral value - Read the balance of collateral c or aToken and use Chainlink to price|Collateral value - call [getTotalAbsPositionValue](https://github.com/perpetual-protocol/perp-lushan/blob/main/contracts/AccountBalance.sol#L299)| +|Debt value - Read the balance of variable debt on Aave or borrowBalanceOf on cToken|Debt value - call [getTotalDebtValue](https://github.com/perpetual-protocol/perp-lushan/blob/main/contracts/AccountBalance.sol#L198)| ### Perp Module Interface This is the current iteration of the interface we are thinking of for trading on the perp module: ```solidity -function trade( - ISetToken _setToken, - address _baseToken, - address _quoteToken, - int256 _quantity, // (10**18) - int256 _minReceiveQuantity, // (10**18) - bytes memory _data -) +lever(ISetToken setToken, uint256 quoteUnits, uint256 quoteMinReceiveUnits) + +delever(ISetToken setToken, uint256 quoteUnits, uint256 quoteMinReceiveUnits) ``` ## Open Questions @@ -57,6 +53,7 @@ function trade( - [ ] Should we keep the ripcord / safety mechanism the same? - Yes. No good reason to change it and gas fees are much lower on L2 so incentive can be way lower - [ ] What functions are needed to derive the state from Perp? + - getTotalAbsPositionValue, getImRatio, getMmRatio, getTotalDebt - [ ] Can this be generalized to use FST and PERP? - [ ] Can we make an assumption that spot and perp prices are going to be very close? From b0c2bcbcca69550ae48cb485e5538dd56462cc54 Mon Sep 17 00:00:00 2001 From: Richard Liang Date: Fri, 12 Nov 2021 10:59:54 -0800 Subject: [PATCH 5/6] Update checkpoint 3 --- STIPS/STIP-007.md | 109 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 93 insertions(+), 16 deletions(-) diff --git a/STIPS/STIP-007.md b/STIPS/STIP-007.md index 0904762..f854504 100644 --- a/STIPS/STIP-007.md +++ b/STIPS/STIP-007.md @@ -38,13 +38,24 @@ In general, external perpetual protocols simplify the lever and delever flows fo |Max borrow - Retrieve the collateralFactorMantissa in Compound and maxLtv and liquidationThreshold in Aave|Max "borrow" - call [getImRatio()](https://github.com/perpetual-protocol/perp-lushan/blob/c6b6a3810bdb37534d6931b8be24c5de0bbd122c/contracts/ClearingHouseConfig.sol#L91-L96) and (1 - imRatio) to get the "maxLtv" and call [getMmRatio()](https://github.com/perpetual-protocol/perp-lushan/blob/c6b6a3810bdb37534d6931b8be24c5de0bbd122c/contracts/ClearingHouseConfig.sol#L91-L96) and (1 - mmRatio) to get the liquidation threshold | |Collateral value - Read the balance of collateral c or aToken and use Chainlink to price|Collateral value - call [getTotalAbsPositionValue](https://github.com/perpetual-protocol/perp-lushan/blob/main/contracts/AccountBalance.sol#L299)| |Debt value - Read the balance of variable debt on Aave or borrowBalanceOf on cToken|Debt value - call [getTotalDebtValue](https://github.com/perpetual-protocol/perp-lushan/blob/main/contracts/AccountBalance.sol#L198)| +|Leverage ratio - Collateral / (Collateral - Debt value)|Leverage ratio - AccountBalance.getTotalAbsPositionValue / ClearingHouse.accountValue | ### Perp Module Interface This is the current iteration of the interface we are thinking of for trading on the perp module: ```solidity -lever(ISetToken setToken, uint256 quoteUnits, uint256 quoteMinReceiveUnits) +function lever( + ISetToken _setToken, + address _baseToken, + int256 _baseQuantityUnits, + uint256 _minReceiveQuantityUnits +) -delever(ISetToken setToken, uint256 quoteUnits, uint256 quoteMinReceiveUnits) +function delever( + ISetToken _setToken, + address _baseToken, + int256 _baseQuantityUnits, + uint256 _minReceiveQuantityUnits +) ``` ## Open Questions @@ -53,7 +64,7 @@ delever(ISetToken setToken, uint256 quoteUnits, uint256 quoteMinReceiveUnits) - [ ] Should we keep the ripcord / safety mechanism the same? - Yes. No good reason to change it and gas fees are much lower on L2 so incentive can be way lower - [ ] What functions are needed to derive the state from Perp? - - getTotalAbsPositionValue, getImRatio, getMmRatio, getTotalDebt + - getTotalAbsPositionValue, getImRatio, getMmRatio, accountValue - [ ] Can this be generalized to use FST and PERP? - [ ] Can we make an assumption that spot and perp prices are going to be very close? @@ -104,7 +115,7 @@ Before more in depth design of the contract flows lets make sure that all the wo A keeper wants to rebalance the Perp token which last rebalanced a day ago so calls shouldRebalance on the strategy contract directly - The keeper calls `rebalance` on the PerpStrategyExtension - The exchange's max trade size is grabbed from storage -- The account leverage / max leverage is grabbed from the Perp Module's helper functions. This is used to calculate the current leverage ratio +- The account leverage (AccountBalance.getTotalAbsPositionValue / ClearingHouse.accountValue) and max leverage (getImRatio for levering, getMmRatio for delevering) is grabbed from Perp V2 - Validate leverage ratio isn't above ripcord and that enough time has elapsed since lastTradeTimestamp (unless outside bounds) - Validate not in TWAP - Calculate new leverage ratio using Perp helpers @@ -148,33 +159,99 @@ Before we spec out the contract(s) in depth we want to make sure that we are ali Reviewer: [] ## Specification -### [Contract Name] +### PerpetualV2LeverageStrategyExtension #### Inheritance -- List inherited contracts +- BaseExtension #### Structs + +##### ContractSettings | Type | Name | Description | |------ |------ |------------- | |address|manager|Address of the manager| |uint256|iterations|Number of times manager has called contract| -#### Constants -| Type | Name | Description | Value | -|------ |------ |------------- |------- | -|uint256|ONE | The number one| 1 | + #### Public Variables | Type | Name | Description | |------ |------ |------------- | -|uint256|hodlers|Number of holders of this token| +|ContractSettings|strategy|Struct of contracts used in the strategy (SetToken, price oracles, leverage module etc)| +|MethodologySettings|methodology|Struct containing methodology parameters| +|ExecutionSettings|execution|Struct containing execution parameters| +|ExchangeSettings|exchange|Struct containing exchange settings| +|IncentiveSettings|incentive| Struct containing incentive parameters for ripcord| +|uint256|twapLeverageRatio|Stored leverage ratio to keep track of target between TWAP rebalances| +|uint256|globalLastTradeTimestamp|Stored last trade timestamp| + #### Functions | Name | Caller | Description | |------ |------ |------------- | -|startRebalance|Manager|Set rebalance parameters| -|rebalance|Trader|Rebalance SetToken| +|engage|Operator|Lever up SetToken| +|disengage|Operator|Delever SetToken| +|rebalance|EOA|Rebalance SetToken| +|iterateRebalance|EOA|Iterate rebalance on SetToken| |ripcord|EOA|Recenter leverage ratio| +|setMethodologySettings|Operator|Set methodology settings| +|setIncentiveSettings|Operator|Set incentive settings| +|setExchangeSettings|Operator|Set exchange settings| +|setExecutionSettings|Operator|Set execution settings| +|shouldRebalance|Anyone|Check if Set is ready to be rebalanced| #### Modifiers -> onlyManager(SetToken _setToken) +``` +/** + * Throws if rebalance is currently in TWAP` + */ +modifier noRebalanceInProgress() { + require(twapLeverageRatio == 0, "Rebalance is currently in progress"); + _; +} +``` #### Functions -> issue(SetToken _setToken, uint256 quantity) external -- Pseudo code +> rebalance() external onlyEOA +- Create action info struct + - Fetch collateral price + - Fetch borrow price + - Fetch account value in PERP + - Fetch total absolute position value in PERP + - Calculate current leverage (total position value / account value) +- Validate normal rebalance / non TWAP +- Calculate new leverage ratio +- Handle rebalance + - Calculate chunk total notional + - Calculate max borrow (imRatio for lever, mmRatio for delever) + - Lever or delever on PerpModule depending on leverage ratio +- Update rebalance state +- Emit event + +> iterateRebalance() external onlyEOA +- Create action info struct + - Fetch collateral price + - Fetch borrow price + - Fetch account value in PERP + - Fetch total absolute position value in PERP + - Calculate current leverage (total position value / account value) +- Validate normal rebalance / is TWAP +- Check if advantageous TWAP and exit +- Handle rebalance + - Calculate chunk total notional + - Calculate max borrow (imRatio for lever, mmRatio for delever) + - Lever or delever on PerpModule depending on leverage ratio +- Update iterate state +- Emit event + +> ripcord() external onlyEOA +- Create action info struct + - Fetch collateral price + - Fetch borrow price + - Fetch account value in PERP + - Fetch total absolute position value in PERP + - Calculate current leverage (total position value / account value) +- Validate Ripcord +- Calculate chunk total notional + - Calculate max borrow (imRatio for lever, mmRatio for delever) +- Delever +- Update ripcord state +- Send ETH reward to caller +- Emit event + ## Checkpoint 3 Before we move onto the implementation phase we want to make sure that we are aligned on the spec. All contracts should be specced out, their state and external function signatures should be defined. For more complex contracts, internal function definition is preferred in order to align on proper abstractions. Reviewer should take care to make sure that all stake holders (product, app engineering) have their needs met in this stage. From bb77e337e612d8de1881482bfe25dd8f01d6205b Mon Sep 17 00:00:00 2001 From: Richard Liang Date: Wed, 24 Nov 2021 09:59:39 -0800 Subject: [PATCH 6/6] Update STIP --- STIPS/STIP-007.md | 92 ++++++++++++++++++++++++----------------------- 1 file changed, 47 insertions(+), 45 deletions(-) diff --git a/STIPS/STIP-007.md b/STIPS/STIP-007.md index f854504..34d3176 100644 --- a/STIPS/STIP-007.md +++ b/STIPS/STIP-007.md @@ -41,21 +41,14 @@ In general, external perpetual protocols simplify the lever and delever flows fo |Leverage ratio - Collateral / (Collateral - Debt value)|Leverage ratio - AccountBalance.getTotalAbsPositionValue / ClearingHouse.accountValue | ### Perp Module Interface -This is the current iteration of the interface we are thinking of for trading on the perp module: +This is the current interface for the perp module: ```solidity -function lever( +function trade( ISetToken _setToken, address _baseToken, int256 _baseQuantityUnits, - uint256 _minReceiveQuantityUnits -) - -function delever( - ISetToken _setToken, - address _baseToken, - int256 _baseQuantityUnits, - uint256 _minReceiveQuantityUnits -) + uint256 _receiveQuoteQuantityUnits +) external; ``` ## Open Questions @@ -65,7 +58,11 @@ function delever( - Yes. No good reason to change it and gas fees are much lower on L2 so incentive can be way lower - [ ] What functions are needed to derive the state from Perp? - getTotalAbsPositionValue, getImRatio, getMmRatio, accountValue +- [ ] Do we need to take into account max borrow if max leverage is 10x? + - No, we can remove this logic that was previously in ALM and CLM + - getTotalAbsPositionValue, getImRatio, getMmRatio, accountValue - [ ] Can this be generalized to use FST and PERP? + - No - [ ] Can we make an assumption that spot and perp prices are going to be very close? ## Feasibility Analysis @@ -76,6 +73,7 @@ function delever( - No need for a viewer contract - Use Chainlink oracles to price slippage and determine trade size. We need to assume that perp and spot prices will be within a reasonable % - Retrieve leverage and max borrow calculations from Perp directly vs using Chainlink oracles on our own +- Allow negative leverage ratios for short positions against USDC Uses the existing strategy extension structure. We can always create new extension contracts and upgrade. @@ -100,8 +98,8 @@ Before more in depth design of the contract flows lets make sure that all the wo - Use Chainlink to retrieve oracle prices to price slippage - Use PerpModule view function instead to calculate account leverage - Updates data structures to reflect new contracts -- Update calculateMaxBorrow -- Update calculateCurrentLeverageRatio +- Remove calculateMaxBorrow +- Update all variables to allow negative numbers ![flow diagram](../assets/perpetualLeverageStrategy.png) @@ -115,14 +113,13 @@ Before more in depth design of the contract flows lets make sure that all the wo A keeper wants to rebalance the Perp token which last rebalanced a day ago so calls shouldRebalance on the strategy contract directly - The keeper calls `rebalance` on the PerpStrategyExtension - The exchange's max trade size is grabbed from storage -- The account leverage (AccountBalance.getTotalAbsPositionValue / ClearingHouse.accountValue) and max leverage (getImRatio for levering, getMmRatio for delevering) is grabbed from Perp V2 -- Validate leverage ratio isn't above ripcord and that enough time has elapsed since lastTradeTimestamp (unless outside bounds) +- The balance of the baseToken and quoteToken (USDC) are grabbed from Perp V2 +- Validate absolute value of leverage ratio isn't above ripcord and that enough time has elapsed since lastTradeTimestamp (unless outside bounds) - Validate not in TWAP -- Calculate new leverage ratio using Perp helpers -- Calculate the total rebalance size and the chunk rebalance size, the chunk rebalance size is less than total rebalance size -- Use Chainlink oracles -- Read max leverage parameter from perp exchange and ensure that we are below it -- Create calldata for invoking trade on PerpModule +- Calculate new leverage ratio +- Calculate the total rebalance size and the chunk rebalance size (negative for short tokens), the chunk rebalance size is less than total rebalance size +- Use Chainlink oracles to calculate slippage in quoteToken (USDC) +- Create calldata for invoking trade on PerpModule. Pass in baseToken, minQuoteTokenReceived, positionUnits traded (negative for shorts) - Log the last trade timestamp - Set the twapLeverageRatio so that we don't have to recalculate the target leverage ratio again for this TWAP @@ -130,10 +127,10 @@ A keeper wants to rebalance the Perp token which last rebalanced a day ago so ca 1. A keeper wants to rebalance the token which kicked off a TWAP rebalance one block ago so calls shouldRebalance which tells it to iterate 2. The keeper calls `iterateRebalance` 3. The max trade size and last trade timestamp are grabbed from storage -4. Validate leverage ratio isn't above ripcord and that enough time has elapsed since lastTradeTimestamp -5. Validate leverage ratio isn't above ripcord and that enough time has elapsed since last lastTradeTimestamp +4. Validate abs of leverage ratio isn't above ripcord and that enough time has elapsed since lastTradeTimestamp +5. Validate abs of leverage ratio isn't above ripcord and that enough time has elapsed since last lastTradeTimestamp 6. Validate TWAP is underway -7. Calculate new leverage ratio using Perp helpers +7. Calculate new leverage ratio 8. Check that prices haven't moved making continuing rebalance unnecessary 9. Calculate the total rebalance size and the chunk rebalance size, the chunk rebalance size equals total rebalance size 10. Create calldata for invoking lever/delever @@ -145,9 +142,9 @@ A keeper wants to rebalance the Perp token which last rebalanced a day ago so ca 2. The keeper calls `ripcord` 3. The incentivized max trade size and last trade timestamp are grabbed from storage 4. The current leverage ratio is calculated and all params are gathered for the rebalance including incentivized trade size -5. Validate that leverage ratio outside bounds and that incentivized cool down period has elapsed from lastTradeTimestamp +5. Validate that abs leverage ratio outside bounds and that incentivized cool down period has elapsed from lastTradeTimestamp 6. Calculate the notional amount of the chunk rebalance size -7. Create calldata for invoking delever on CLM +7. Create calldata for trading 8. Log last trade timestamp 9. Delete twapLeverageRatio 10. Transfer ether to keeper @@ -167,8 +164,13 @@ Reviewer: [] ##### ContractSettings | Type | Name | Description | |------ |------ |------------- | -|address|manager|Address of the manager| -|uint256|iterations|Number of times manager has called contract| +|ISetToken|setToken|Instance of leverage token| +|IPerpV2LeverageModule|perpV2LeverageModule|Instance of Perp V2 leverage module| +|IAccountBalance|perpV2AccountBalance|Instance of Perp V2 AccountBalance contract used to fetch position balances| +|IChainlinkAggregatorV3|basePriceOracle|Chainlink oracle feed that returns prices in 8 decimals for collateral asset| +|IChainlinkAggregatorV3|quotePriceOracle|Chainlink oracle feed that returns prices in 8 decimals for collateral asset| +|address|virtualBaseAddress|Address of virtual base asset (e.g. vETH, vWBTC etc)| +|address|virtualQuoteAddress|Address of virtual USDC quote asset. The Perp V2 system uses USDC for all markets| #### Public Variables | Type | Name | Description | @@ -207,46 +209,46 @@ modifier noRebalanceInProgress() { #### Functions > rebalance() external onlyEOA - Create action info struct - - Fetch collateral price - - Fetch borrow price - - Fetch account value in PERP - - Fetch total absolute position value in PERP - - Calculate current leverage (total position value / account value) + - Fetch base token price + - Fetch quote token price + - Fetch base token value in PERP + - Fetch quote token value in PERP + - Calculate current leverage - Validate normal rebalance / non TWAP - Calculate new leverage ratio - Handle rebalance - Calculate chunk total notional - - Calculate max borrow (imRatio for lever, mmRatio for delever) + - Calculate max quote token (imRatio for lever, mmRatio for delever) - Lever or delever on PerpModule depending on leverage ratio - Update rebalance state - Emit event > iterateRebalance() external onlyEOA - Create action info struct - - Fetch collateral price - - Fetch borrow price - - Fetch account value in PERP - - Fetch total absolute position value in PERP - - Calculate current leverage (total position value / account value) + - Fetch base token price + - Fetch quote token price + - Fetch base token value in PERP + - Fetch quote token value in PERP + - Calculate current leverage - Validate normal rebalance / is TWAP - Check if advantageous TWAP and exit - Handle rebalance - Calculate chunk total notional - - Calculate max borrow (imRatio for lever, mmRatio for delever) + - Calculate max quote token (imRatio for lever, mmRatio for delever) - Lever or delever on PerpModule depending on leverage ratio - Update iterate state - Emit event > ripcord() external onlyEOA - Create action info struct - - Fetch collateral price - - Fetch borrow price - - Fetch account value in PERP - - Fetch total absolute position value in PERP - - Calculate current leverage (total position value / account value) + - Fetch base token price + - Fetch quote token price + - Fetch base token value in PERP + - Fetch quote token value in PERP + - Calculate current leverage - Validate Ripcord - Calculate chunk total notional - - Calculate max borrow (imRatio for lever, mmRatio for delever) + - Calculate max quote token (imRatio for lever, mmRatio for delever) - Delever - Update ripcord state - Send ETH reward to caller