@dolomite-exchange/dolomite-margin 0.2.6 → 0.2.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (200) hide show
  1. package/build/contracts/Account.json +11 -11
  2. package/build/contracts/Actions.json +11 -11
  3. package/build/contracts/Address.json +1 -1
  4. package/build/contracts/Admin.json +23 -23
  5. package/build/contracts/AdminImpl.json +35 -307
  6. package/build/contracts/AdvancedMath.json +7 -7
  7. package/build/contracts/AmmRebalancerProxy.json +577 -621
  8. package/build/contracts/AmmRebalancerProxyV1.json +78 -32
  9. package/build/contracts/Babylonian.json +1 -1
  10. package/build/contracts/Bits.json +9 -9
  11. package/build/contracts/Cache.json +13 -13
  12. package/build/contracts/ChainlinkPriceOracleV1.json +16 -16
  13. package/build/contracts/Context.json +1 -1
  14. package/build/contracts/CustomTestToken.json +9 -9
  15. package/build/contracts/Decimal.json +9 -9
  16. package/build/contracts/DelayedMultiSig.json +9 -9
  17. package/build/contracts/DolomiteAmmERC20.json +11 -11
  18. package/build/contracts/DolomiteAmmFactory.json +14 -52
  19. package/build/contracts/DolomiteAmmLibrary.json +24 -24
  20. package/build/contracts/DolomiteAmmPair.json +27 -27
  21. package/build/contracts/DolomiteAmmRouterProxy.json +30 -363
  22. package/build/contracts/DolomiteMargin.json +21 -67
  23. package/build/contracts/DoubleExponentInterestSetter.json +15 -21
  24. package/build/contracts/EnumerableSet.json +7 -7
  25. package/build/contracts/ErroringOmiseToken.json +9 -9
  26. package/build/contracts/ErroringToken.json +10 -10
  27. package/build/contracts/Events.json +17 -17
  28. package/build/contracts/ExcessivelySafeCall.json +35 -35
  29. package/build/contracts/Exchange.json +15 -15
  30. package/build/contracts/Expiry.json +36 -36
  31. package/build/contracts/Getters.json +33 -33
  32. package/build/contracts/IAutoTrader.json +9 -9
  33. package/build/contracts/ICallee.json +7 -7
  34. package/build/contracts/IChainlinkAggregator.json +5 -5
  35. package/build/contracts/IDolomiteAmmERC20.json +5 -5
  36. package/build/contracts/IDolomiteAmmFactory.json +9 -9
  37. package/build/contracts/IDolomiteAmmPair.json +9 -9
  38. package/build/contracts/IDolomiteMargin.json +23 -23
  39. package/build/contracts/IERC20.json +1 -1
  40. package/build/contracts/IERC20Detailed.json +5 -5
  41. package/build/contracts/IExchangeWrapper.json +5 -5
  42. package/build/contracts/IExpiry.json +9 -9
  43. package/build/contracts/IInterestSetter.json +7 -7
  44. package/build/contracts/ILiquidationCallback.json +7 -7
  45. package/build/contracts/IMakerOracle.json +5 -5
  46. package/build/contracts/IOasisDex.json +5 -5
  47. package/build/contracts/IPriceOracle.json +7 -7
  48. package/build/contracts/IRecyclable.json +9 -9
  49. package/build/contracts/ITransferProxy.json +5 -5
  50. package/build/contracts/IUniswapV2Callee.json +5 -5
  51. package/build/contracts/IUniswapV2Factory.json +5 -5
  52. package/build/contracts/IUniswapV2Pair.json +5 -5
  53. package/build/contracts/IUniswapV2Router.json +5 -5
  54. package/build/contracts/IWETH.json +5 -5
  55. package/build/contracts/Interest.json +15 -15
  56. package/build/contracts/LiquidateOrVaporizeImpl.json +32 -83
  57. package/build/contracts/LiquidatorProxyHelper.json +23 -23
  58. package/build/contracts/LiquidatorProxyV1.json +30 -36
  59. package/build/contracts/LiquidatorProxyV1WithAmm.json +49 -105
  60. package/build/contracts/Math.json +1 -1
  61. package/build/contracts/Migrations.json +8 -14
  62. package/build/contracts/Monetary.json +7 -7
  63. package/build/contracts/MultiCall.json +8 -14
  64. package/build/contracts/MultiSig.json +7 -7
  65. package/build/contracts/OmiseToken.json +8 -8
  66. package/build/contracts/OnlyDolomiteMargin.json +11 -11
  67. package/build/contracts/Operation.json +15 -15
  68. package/build/contracts/OperationImpl.json +39 -47
  69. package/build/contracts/Ownable.json +1 -1
  70. package/build/contracts/PartiallyDelayedMultiSig.json +9 -9
  71. package/build/contracts/PayableProxy.json +17 -23
  72. package/build/contracts/Permission.json +11 -11
  73. package/build/contracts/PolynomialInterestSetter.json +13 -13
  74. package/build/contracts/RecyclableTokenProxy.json +25 -25
  75. package/build/contracts/ReentrancyGuard.json +1 -1
  76. package/build/contracts/Require.json +7 -7
  77. package/build/contracts/SafeERC20.json +1 -1
  78. package/build/contracts/SafeETH.json +7 -7
  79. package/build/contracts/SafeLiquidationCallback.json +17 -17
  80. package/build/contracts/SafeMath.json +1 -1
  81. package/build/contracts/SignedOperationProxy.json +21 -104
  82. package/build/contracts/SimpleFeeOwner.json +18 -63
  83. package/build/contracts/State.json +9 -9
  84. package/build/contracts/Storage.json +37 -37
  85. package/build/contracts/TestAmmRebalancerProxy.json +32 -58
  86. package/build/contracts/TestAutoTrader.json +22 -22
  87. package/build/contracts/TestBtcUsdChainlinkAggregator.json +10 -10
  88. package/build/contracts/TestCallee.json +19 -19
  89. package/build/contracts/TestChainlinkPriceOracleV1.json +10 -94
  90. package/build/contracts/TestCounter.json +7 -7
  91. package/build/contracts/TestDaiUsdChainlinkAggregator.json +11 -11
  92. package/build/contracts/TestDolomiteMargin.json +22 -22
  93. package/build/contracts/TestDoubleExponentInterestSetter.json +10 -10
  94. package/build/contracts/TestEthUsdChainlinkAggregator.json +11 -11
  95. package/build/contracts/TestExchangeWrapper.json +28 -28
  96. package/build/contracts/TestInterestSetter.json +13 -13
  97. package/build/contracts/TestLib.json +22 -22
  98. package/build/contracts/TestLinkUsdChainlinkAggregator.json +11 -11
  99. package/build/contracts/TestLiquidateCallback.json +719 -719
  100. package/build/contracts/TestLiquidationCallback.json +13 -13
  101. package/build/contracts/TestLrcEthChainlinkAggregator.json +11 -11
  102. package/build/contracts/TestMakerOracle.json +10 -10
  103. package/build/contracts/TestMaticUsdChainlinkAggregator.json +10 -10
  104. package/build/contracts/TestOasisDex.json +12 -12
  105. package/build/contracts/TestOperationImpl.json +14 -14
  106. package/build/contracts/TestPolynomialInterestSetter.json +10 -10
  107. package/build/contracts/TestPriceAggregator.json +9 -9
  108. package/build/contracts/TestPriceOracle.json +13 -13
  109. package/build/contracts/TestRecyclableToken.json +9 -9
  110. package/build/contracts/TestSimpleCallee.json +15 -15
  111. package/build/contracts/TestToken.json +9 -9
  112. package/build/contracts/TestTrader.json +17 -17
  113. package/build/contracts/TestUniswapAmmRebalancerProxy.json +16 -16
  114. package/build/contracts/TestUsdcUsdChainlinkAggregator.json +11 -11
  115. package/build/contracts/TestWETH.json +9 -9
  116. package/build/contracts/Time.json +9 -9
  117. package/build/contracts/Token.json +9 -9
  118. package/build/contracts/TokenA.json +10 -10
  119. package/build/contracts/TokenB.json +10 -10
  120. package/build/contracts/TokenC.json +10 -10
  121. package/build/contracts/TokenD.json +10 -10
  122. package/build/contracts/TokenE.json +10 -10
  123. package/build/contracts/TokenF.json +10 -10
  124. package/build/contracts/TransferProxy.json +22 -28
  125. package/build/contracts/TypedSignature.json +9 -9
  126. package/build/contracts/Types.json +9 -9
  127. package/build/contracts/UQ112x112.json +7 -7
  128. package/build/contracts/UniswapV2ERC20.json +7 -7
  129. package/build/contracts/UniswapV2Factory.json +12 -12
  130. package/build/contracts/UniswapV2Library.json +20 -20
  131. package/build/contracts/UniswapV2Pair.json +17 -17
  132. package/build/contracts/UniswapV2Router02.json +16 -16
  133. package/build/contracts/WETH9.json +1 -1
  134. package/contracts/external/amm/DolomiteAmmERC20.sol +135 -0
  135. package/contracts/external/amm/DolomiteAmmFactory.sol +122 -0
  136. package/contracts/external/amm/DolomiteAmmPair.sol +573 -0
  137. package/contracts/external/amm/SimpleFeeOwner.sol +107 -0
  138. package/contracts/external/helpers/LiquidatorProxyHelper.sol +252 -0
  139. package/contracts/external/helpers/OnlyDolomiteMargin.sol +63 -0
  140. package/contracts/external/interestsetters/DoubleExponentInterestSetter.sol +212 -0
  141. package/contracts/external/interestsetters/PolynomialInterestSetter.sol +205 -0
  142. package/contracts/external/interfaces/IChainlinkAggregator.sol +33 -0
  143. package/contracts/external/interfaces/IDolomiteAmmERC20.sol +52 -0
  144. package/contracts/external/interfaces/IDolomiteAmmFactory.sol +42 -0
  145. package/contracts/external/interfaces/IDolomiteAmmPair.sol +116 -0
  146. package/contracts/external/interfaces/IExpiry.sol +70 -0
  147. package/contracts/external/interfaces/IMakerOracle.sol +52 -0
  148. package/contracts/external/interfaces/IOasisDex.sol +326 -0
  149. package/contracts/external/interfaces/ITransferProxy.sol +53 -0
  150. package/contracts/external/interfaces/IUniswapV2Router.sol +134 -0
  151. package/contracts/external/lib/AdvancedMath.sol +23 -0
  152. package/contracts/external/lib/DolomiteAmmLibrary.sol +323 -0
  153. package/contracts/external/lib/TypedSignature.sol +120 -0
  154. package/contracts/external/lib/UQ112x112.sol +22 -0
  155. package/contracts/external/multisig/DelayedMultiSig.sol +206 -0
  156. package/contracts/external/multisig/MultiSig.sol +571 -0
  157. package/contracts/external/multisig/PartiallyDelayedMultiSig.sol +174 -0
  158. package/contracts/external/oracles/ChainlinkPriceOracleV1.sol +197 -0
  159. package/contracts/external/oracles/TestChainlinkPriceOracleV1.sol +98 -0
  160. package/contracts/external/proxies/AmmRebalancerProxyV1.sol +465 -0
  161. package/contracts/external/proxies/DolomiteAmmRouterProxy.sol +877 -0
  162. package/contracts/external/proxies/LiquidatorProxyV1.sol +507 -0
  163. package/contracts/external/proxies/LiquidatorProxyV1WithAmm.sol +574 -0
  164. package/contracts/external/proxies/PayableProxy.sol +146 -0
  165. package/contracts/external/proxies/RecyclableTokenProxy.sol +463 -0
  166. package/contracts/external/proxies/SignedOperationProxy.sol +553 -0
  167. package/contracts/external/proxies/TransferProxy.sol +207 -0
  168. package/contracts/external/traders/Expiry.sol +532 -0
  169. package/contracts/external/uniswap-v2/UniswapV2ERC20.sol +118 -0
  170. package/contracts/external/uniswap-v2/UniswapV2Factory.sol +67 -0
  171. package/contracts/external/uniswap-v2/UniswapV2Pair.sol +283 -0
  172. package/contracts/external/uniswap-v2/UniswapV2Router02.sol +566 -0
  173. package/contracts/external/uniswap-v2/interfaces/IUniswapV2Callee.sol +13 -0
  174. package/contracts/external/uniswap-v2/interfaces/IUniswapV2Factory.sol +18 -0
  175. package/contracts/external/uniswap-v2/interfaces/IUniswapV2Pair.sol +67 -0
  176. package/contracts/external/uniswap-v2/interfaces/IWETH.sol +7 -0
  177. package/contracts/external/uniswap-v2/libraries/SafeETH.sol +29 -0
  178. package/contracts/external/uniswap-v2/libraries/UniswapV2Library.sol +117 -0
  179. package/contracts/external/utils/MultiCall.sol +95 -0
  180. package/contracts/protocol/impl/artifacts/OperationImpl.json +80 -0
  181. package/contracts/protocol/impl/artifacts/OperationImpl_metadata.json +193 -0
  182. package/dist/build/published_contracts/AdminImpl.json +2 -2
  183. package/dist/build/published_contracts/AmmRebalancerProxyV1.json +5 -0
  184. package/dist/build/published_contracts/ChainlinkPriceOracleV1.json +1 -1
  185. package/dist/build/published_contracts/DolomiteAmmFactory.json +1 -1
  186. package/dist/build/published_contracts/DolomiteAmmRouterProxy.json +1 -1
  187. package/dist/build/published_contracts/DoubleExponentInterestSetter.json +2 -2
  188. package/dist/build/published_contracts/Expiry.json +1 -1
  189. package/dist/build/published_contracts/LiquidatorProxyV1.json +1 -1
  190. package/dist/build/published_contracts/LiquidatorProxyV1WithAmm.json +1 -1
  191. package/dist/build/published_contracts/MultiCall.json +1 -1
  192. package/dist/build/published_contracts/PayableProxy.json +2 -2
  193. package/dist/build/published_contracts/SignedOperationProxy.json +2 -2
  194. package/dist/build/published_contracts/SimpleFeeOwner.json +1 -1
  195. package/dist/build/published_contracts/TestUniswapAmmRebalancerProxy.json +140 -0
  196. package/dist/build/published_contracts/TransferProxy.json +1 -1
  197. package/dist/src/lib/Contracts.d.ts +3 -1
  198. package/dist/src/lib/Contracts.js +7 -3
  199. package/dist/src/lib/Contracts.js.map +1 -1
  200. package/package.json +2 -1
@@ -0,0 +1,574 @@
1
+ /*
2
+
3
+ Copyright 2019 dYdX Trading Inc.
4
+
5
+ Licensed under the Apache License, Version 2.0 (the "License");
6
+ you may not use this file except in compliance with the License.
7
+ You may obtain a copy of the License at
8
+
9
+ http://www.apache.org/licenses/LICENSE-2.0
10
+
11
+ Unless required by applicable law or agreed to in writing, software
12
+ distributed under the License is distributed on an "AS IS" BASIS,
13
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ See the License for the specific language governing permissions and
15
+ limitations under the License.
16
+
17
+ */
18
+
19
+ pragma solidity ^0.5.7;
20
+ pragma experimental ABIEncoderV2;
21
+
22
+ import { SafeMath } from "@openzeppelin/contracts/math/SafeMath.sol";
23
+ import { ReentrancyGuard } from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
24
+
25
+ import { IDolomiteMargin } from "../../protocol/interfaces/IDolomiteMargin.sol";
26
+
27
+ import { Account } from "../../protocol/lib/Account.sol";
28
+ import { Actions } from "../../protocol/lib/Actions.sol";
29
+ import { Decimal } from "../../protocol/lib/Decimal.sol";
30
+ import { Interest } from "../../protocol/lib/Interest.sol";
31
+ import { Math } from "../../protocol/lib/Math.sol";
32
+ import { Monetary } from "../../protocol/lib/Monetary.sol";
33
+ import { Require } from "../../protocol/lib/Require.sol";
34
+ import { Time } from "../../protocol/lib/Time.sol";
35
+ import { Types } from "../../protocol/lib/Types.sol";
36
+
37
+ import { LiquidatorProxyHelper } from "../helpers/LiquidatorProxyHelper.sol";
38
+ import { IExpiry } from "../interfaces/IExpiry.sol";
39
+
40
+ import { DolomiteAmmRouterProxy } from "./DolomiteAmmRouterProxy.sol";
41
+
42
+
43
+ /**
44
+ * @title LiquidatorProxyV1WithAmm
45
+ * @author Dolomite
46
+ *
47
+ * Contract for liquidating other accounts in DolomiteMargin and atomically selling off collateral via Dolomite AMM
48
+ * markets.
49
+ */
50
+ contract LiquidatorProxyV1WithAmm is ReentrancyGuard, LiquidatorProxyHelper {
51
+ using Math for uint256;
52
+ using SafeMath for uint256;
53
+ using Types for Types.Par;
54
+ using Types for Types.Wei;
55
+
56
+ // ============ Constants ============
57
+
58
+ bytes32 constant FILE = "LiquidatorProxyV1WithAmm";
59
+
60
+ // ============ Structs ============
61
+
62
+ struct Constants {
63
+ IDolomiteMargin dolomiteMargin;
64
+ Account.Info solidAccount;
65
+ Account.Info liquidAccount;
66
+ MarketInfo[] markets;
67
+ uint256[] liquidMarkets;
68
+ IExpiry expiryProxy;
69
+ uint32 expiry;
70
+ }
71
+
72
+ struct LiquidatorProxyWithAmmCache {
73
+ // mutable
74
+ uint256 toLiquidate;
75
+ // The amount of heldMarket the solidAccount will receive. Includes the liquidation reward.
76
+ uint256 solidHeldUpdateWithReward;
77
+ Types.Wei solidHeldWei;
78
+ Types.Wei liquidHeldWei;
79
+ Types.Wei liquidOwedWei;
80
+
81
+ // immutable
82
+ uint256 heldMarket;
83
+ uint256 owedMarket;
84
+ uint256 heldPrice;
85
+ uint256 owedPrice;
86
+ uint256 owedPriceAdj;
87
+ }
88
+
89
+ // ============ Events ============
90
+
91
+ /**
92
+ * @param solidAccountOwner The liquidator's address
93
+ * @param solidAccountOwner The liquidator's account number
94
+ * @param heldMarket The held market (collateral) that will be received by the liquidator
95
+ * @param heldDeltaWeiWithReward The amount of heldMarket the liquidator will receive, including the reward
96
+ * (positive number)
97
+ * @param profitHeldWei The amount of profit the liquidator will realize by performing the liquidation
98
+ * and atomically selling off the collateral. Can be negative or positive.
99
+ * @param owedMarket The debt market that will be received by the liquidator
100
+ * @param owedDeltaWei The amount of owedMarket that will be received by the liquidator (negative
101
+ * number)
102
+ */
103
+ event LogLiquidateWithAmm(
104
+ address indexed solidAccountOwner,
105
+ uint solidAccountNumber,
106
+ uint heldMarket,
107
+ uint heldDeltaWeiWithReward,
108
+ Types.Wei profitHeldWei, // calculated as `heldWeiWithReward - soldHeldWeiToBreakEven`
109
+ uint owedMarket,
110
+ uint owedDeltaWei
111
+ );
112
+
113
+ // ============ Storage ============
114
+
115
+ IDolomiteMargin DOLOMITE_MARGIN;
116
+ DolomiteAmmRouterProxy ROUTER_PROXY;
117
+ IExpiry EXPIRY_PROXY;
118
+
119
+ // ============ Constructor ============
120
+
121
+ constructor (
122
+ address dolomiteMargin,
123
+ address dolomiteAmmRouterProxy,
124
+ address expiryProxy
125
+ )
126
+ public
127
+ {
128
+ DOLOMITE_MARGIN = IDolomiteMargin(dolomiteMargin);
129
+ ROUTER_PROXY = DolomiteAmmRouterProxy(dolomiteAmmRouterProxy);
130
+ EXPIRY_PROXY = IExpiry(expiryProxy);
131
+ }
132
+
133
+ // ============ Public Functions ============
134
+
135
+ /**
136
+ * Liquidate liquidAccount using solidAccount. This contract and the msg.sender to this contract
137
+ * must both be operators for the solidAccount.
138
+ *
139
+ * @param solidAccount The account that will do the liquidating
140
+ * @param liquidAccount The account that will be liquidated
141
+ * @param owedMarket The owed market whose borrowed value will be added to `toLiquidate`
142
+ * @param heldMarket The held market whose collateral will be recovered to take on the debt of
143
+ * `owedMarket`
144
+ * @param tokenPath The path through which the trade will be routed to recover the collateral
145
+ * @param expiry The time at which the position expires, if this liquidation is for closing
146
+ * an expired position. Else, 0.
147
+ * @param minOwedOutputAmount The minimum amount that should be outputted by the trade from heldWei to
148
+ * owedWei. Used to prevent sandwiching and mem-pool other attacks. Only used
149
+ * if `revertOnFailToSellCollateral` is set to `false` and the collateral
150
+ * cannot cover the `liquidAccount`'s debt.
151
+ * @param revertOnFailToSellCollateral True to revert the transaction completely if all collateral from the
152
+ * liquidation cannot repay the owed debt. False to swallow the error and sell
153
+ * whatever is possible. If set to false, the liquidator must have sufficient
154
+ * assets to be prevent becoming liquidated or under-collateralized.
155
+ */
156
+ function liquidate(
157
+ Account.Info memory solidAccount,
158
+ Account.Info memory liquidAccount,
159
+ uint256 owedMarket,
160
+ uint256 heldMarket,
161
+ address[] memory tokenPath,
162
+ uint expiry,
163
+ uint minOwedOutputAmount,
164
+ bool revertOnFailToSellCollateral
165
+ )
166
+ public
167
+ nonReentrant
168
+ {
169
+ Require.that(
170
+ owedMarket != heldMarket,
171
+ FILE,
172
+ "owedMarket equals heldMarket",
173
+ owedMarket,
174
+ heldMarket
175
+ );
176
+
177
+ Require.that(
178
+ !DOLOMITE_MARGIN.getAccountPar(liquidAccount, owedMarket).isPositive(),
179
+ FILE,
180
+ "owed market cannot be positive",
181
+ owedMarket
182
+ );
183
+
184
+ Require.that(
185
+ DOLOMITE_MARGIN.getAccountPar(liquidAccount, heldMarket).isPositive(),
186
+ FILE,
187
+ "held market cannot be negative",
188
+ heldMarket
189
+ );
190
+
191
+ Require.that(
192
+ uint32(expiry) == expiry,
193
+ FILE,
194
+ "expiry overflow",
195
+ expiry
196
+ );
197
+
198
+ // put all values that will not change into a single struct
199
+ Constants memory constants;
200
+ constants.dolomiteMargin = DOLOMITE_MARGIN;
201
+ constants.solidAccount = solidAccount;
202
+ constants.liquidAccount = liquidAccount;
203
+ constants.liquidMarkets = constants.dolomiteMargin.getAccountMarketsWithNonZeroBalances(liquidAccount);
204
+ constants.markets = getMarketInfos(
205
+ constants.dolomiteMargin,
206
+ constants.dolomiteMargin.getAccountMarketsWithNonZeroBalances(solidAccount),
207
+ constants.liquidMarkets
208
+ );
209
+ constants.expiryProxy = expiry > 0 ? EXPIRY_PROXY: IExpiry(address(0));
210
+ constants.expiry = uint32(expiry);
211
+
212
+ LiquidatorProxyWithAmmCache memory cache = initializeCache(
213
+ constants,
214
+ heldMarket,
215
+ owedMarket
216
+ );
217
+
218
+ // validate the msg.sender and that the liquidAccount can be liquidated
219
+ checkRequirements(
220
+ constants,
221
+ heldMarket,
222
+ owedMarket,
223
+ tokenPath
224
+ );
225
+
226
+ // get the max liquidation amount
227
+ calculateMaxLiquidationAmount(cache);
228
+
229
+ // if nothing to liquidate, do nothing
230
+ Require.that(
231
+ cache.toLiquidate != 0,
232
+ FILE,
233
+ "nothing to liquidate"
234
+ );
235
+
236
+ uint totalSolidHeldWei = cache.solidHeldUpdateWithReward;
237
+ if (cache.solidHeldWei.sign) {
238
+ // If the solid account has held wei, add the amount the solid account will receive from liquidation to its
239
+ // total held wei
240
+ // We do this so we can accurately track how much the solid account has (and will have after the swap), in
241
+ // case we need to input it exactly to Router#getParamsForSwapExactTokensForTokens
242
+ totalSolidHeldWei = totalSolidHeldWei.add(cache.solidHeldWei.value);
243
+ }
244
+
245
+ (
246
+ Account.Info[] memory accounts,
247
+ Actions.ActionArgs[] memory actions
248
+ ) = ROUTER_PROXY.getParamsForSwapTokensForExactTokens(
249
+ constants.solidAccount.owner,
250
+ constants.solidAccount.number,
251
+ uint(- 1), // maxInputWei
252
+ cache.toLiquidate, // the amount of owedMarket that needs to be repaid. Exact output amount
253
+ tokenPath
254
+ );
255
+
256
+ if (cache.solidHeldUpdateWithReward >= actions[0].amount.value) {
257
+ uint profit = cache.solidHeldUpdateWithReward.sub(actions[0].amount.value);
258
+ uint _owedMarket = owedMarket; // used to prevent the "stack too deep" error
259
+ emit LogLiquidateWithAmm(
260
+ constants.solidAccount.owner,
261
+ constants.solidAccount.number,
262
+ heldMarket,
263
+ cache.solidHeldUpdateWithReward,
264
+ Types.Wei(true, profit),
265
+ _owedMarket,
266
+ cache.toLiquidate
267
+ );
268
+ } else {
269
+ Require.that(
270
+ !revertOnFailToSellCollateral,
271
+ FILE,
272
+ "totalSolidHeldWei is too small",
273
+ totalSolidHeldWei,
274
+ actions[0].amount.value
275
+ );
276
+
277
+ // This value needs to be calculated before `actions` is overwritten below with the new swap parameters
278
+ uint profit = actions[0].amount.value.sub(cache.solidHeldUpdateWithReward);
279
+ (accounts, actions) = ROUTER_PROXY.getParamsForSwapExactTokensForTokens(
280
+ constants.solidAccount.owner,
281
+ constants.solidAccount.number,
282
+ totalSolidHeldWei, // inputWei
283
+ minOwedOutputAmount,
284
+ tokenPath
285
+ );
286
+
287
+ uint _owedMarket = owedMarket; // used to prevent the "stack too deep" error
288
+ emit LogLiquidateWithAmm(
289
+ constants.solidAccount.owner,
290
+ constants.solidAccount.number,
291
+ heldMarket,
292
+ cache.solidHeldUpdateWithReward,
293
+ Types.Wei(false, profit),
294
+ _owedMarket,
295
+ cache.toLiquidate
296
+ );
297
+ }
298
+
299
+ accounts = constructAccountsArray(constants, accounts);
300
+
301
+ // execute the liquidations
302
+ constants.dolomiteMargin.operate(
303
+ accounts,
304
+ constructActionsArray(constants, cache, accounts, actions) //solium-disable-line arg-overflow
305
+ );
306
+ }
307
+
308
+ // ============ Calculation Functions ============
309
+
310
+ /**
311
+ * Calculate the additional owedAmount that can be liquidated until the collateralization of the
312
+ * liquidator account reaches the minLiquidatorRatio. By this point, the cache will be set such
313
+ * that the amount of owedMarket is non-positive and the amount of heldMarket is non-negative.
314
+ */
315
+ function calculateMaxLiquidationAmount(
316
+ LiquidatorProxyWithAmmCache memory cache
317
+ )
318
+ private
319
+ pure
320
+ {
321
+ uint liquidHeldValue = cache.heldPrice.mul(cache.liquidHeldWei.value);
322
+ uint liquidOwedValue = cache.owedPriceAdj.mul(cache.liquidOwedWei.value);
323
+ if (liquidHeldValue <= liquidOwedValue) {
324
+ // The user is under-collateralized; there is no reward left to give
325
+ cache.solidHeldUpdateWithReward = cache.liquidHeldWei.value;
326
+ cache.toLiquidate = Math.getPartialRoundUp(cache.liquidHeldWei.value, cache.heldPrice, cache.owedPriceAdj);
327
+ } else {
328
+ cache.solidHeldUpdateWithReward = Math.getPartial(
329
+ cache.liquidOwedWei.value,
330
+ cache.owedPriceAdj,
331
+ cache.heldPrice
332
+ );
333
+ cache.toLiquidate = cache.liquidOwedWei.value;
334
+ }
335
+ }
336
+
337
+ // ============ Helper Functions ============
338
+
339
+ /**
340
+ * Make some basic checks before attempting to liquidate an account.
341
+ * - Require that the msg.sender has the permission to use the liquidator account
342
+ * - Require that the liquid account is liquidatable based on the accounts global value (all assets held and owed,
343
+ * not just what's being liquidated)
344
+ */
345
+ function checkRequirements(
346
+ Constants memory constants,
347
+ uint256 heldMarket,
348
+ uint256 owedMarket,
349
+ address[] memory tokenPath
350
+ )
351
+ private
352
+ view
353
+ {
354
+ // check credentials for msg.sender
355
+ Require.that(
356
+ constants.solidAccount.owner == msg.sender
357
+ || constants.dolomiteMargin.getIsLocalOperator(constants.solidAccount.owner, msg.sender),
358
+ FILE,
359
+ "Sender not operator",
360
+ constants.solidAccount.owner
361
+ );
362
+
363
+ Require.that(
364
+ constants.dolomiteMargin.getMarketIdByTokenAddress(tokenPath[0]) == heldMarket,
365
+ FILE,
366
+ "0-index token path incorrect",
367
+ tokenPath[0]
368
+ );
369
+
370
+ Require.that(
371
+ constants.dolomiteMargin.getMarketIdByTokenAddress(tokenPath[tokenPath.length - 1]) == owedMarket,
372
+ FILE,
373
+ "last-index token path incorrect",
374
+ tokenPath[tokenPath.length - 1]
375
+ );
376
+
377
+ if (constants.expiry == 0) {
378
+ // user is getting liquidated, not expired. Check liquid account is indeed liquid
379
+ (
380
+ Monetary.Value memory liquidSupplyValue,
381
+ Monetary.Value memory liquidBorrowValue
382
+ ) = getAdjustedAccountValues(
383
+ constants.dolomiteMargin,
384
+ constants.markets,
385
+ constants.liquidAccount,
386
+ constants.liquidMarkets
387
+ );
388
+ Require.that(
389
+ liquidSupplyValue.value != 0,
390
+ FILE,
391
+ "Liquid account no supply"
392
+ );
393
+ Require.that(
394
+ constants.dolomiteMargin.getAccountStatus(constants.liquidAccount) == Account.Status.Liquid ||
395
+ !isCollateralized(
396
+ liquidSupplyValue.value,
397
+ liquidBorrowValue.value,
398
+ constants.dolomiteMargin.getMarginRatio()
399
+ ),
400
+ FILE,
401
+ "Liquid account not liquidatable"
402
+ );
403
+ } else {
404
+ // check the expiration is valid; to get here we already know constants.expiry != 0
405
+ uint expiry = constants.expiryProxy.getExpiry(constants.liquidAccount, owedMarket);
406
+ Require.that(
407
+ expiry == constants.expiry,
408
+ FILE,
409
+ "expiry mismatch",
410
+ expiry,
411
+ constants.expiry
412
+ );
413
+ Require.that(
414
+ expiry <= Time.currentTime(),
415
+ FILE,
416
+ "Borrow not yet expired",
417
+ expiry
418
+ );
419
+ }
420
+ }
421
+
422
+ /**
423
+ * Returns true if the supplyValue over-collateralizes the borrowValue by the ratio.
424
+ */
425
+ function isCollateralized(
426
+ uint256 supplyValue,
427
+ uint256 borrowValue,
428
+ Decimal.D256 memory ratio
429
+ )
430
+ private
431
+ pure
432
+ returns (bool)
433
+ {
434
+ uint256 requiredMargin = Decimal.mul(borrowValue, ratio);
435
+ return supplyValue >= borrowValue.add(requiredMargin);
436
+ }
437
+
438
+ // ============ Getter Functions ============
439
+
440
+ /**
441
+ * Pre-populates cache values for some pair of markets.
442
+ */
443
+ function initializeCache(
444
+ Constants memory constants,
445
+ uint256 heldMarket,
446
+ uint256 owedMarket
447
+ )
448
+ private
449
+ view
450
+ returns (LiquidatorProxyWithAmmCache memory)
451
+ {
452
+ MarketInfo memory heldMarketInfo = binarySearch(constants.markets, heldMarket);
453
+ MarketInfo memory owedMarketInfo = binarySearch(constants.markets, owedMarket);
454
+ uint256 heldPrice = heldMarketInfo.price.value;
455
+ uint256 owedPrice = owedMarketInfo.price.value;
456
+
457
+ uint256 owedPriceAdj;
458
+ if (constants.expiry > 0) {
459
+ (, Monetary.Price memory owedPricePrice) = constants.expiryProxy.getSpreadAdjustedPrices(
460
+ heldMarket,
461
+ owedMarket,
462
+ constants.expiry
463
+ );
464
+ owedPriceAdj = owedPricePrice.value;
465
+ } else {
466
+ owedPriceAdj = Decimal.mul(
467
+ owedPrice,
468
+ Decimal.onePlus(constants.dolomiteMargin.getLiquidationSpreadForPair(heldMarket, owedMarket))
469
+ );
470
+ }
471
+
472
+ return LiquidatorProxyWithAmmCache({
473
+ toLiquidate: 0,
474
+ solidHeldUpdateWithReward: 0,
475
+ solidHeldWei: Interest.parToWei(
476
+ constants.dolomiteMargin.getAccountPar(constants.solidAccount, heldMarket),
477
+ heldMarketInfo.index
478
+ ),
479
+ liquidHeldWei: Interest.parToWei(
480
+ constants.dolomiteMargin.getAccountPar(constants.liquidAccount, heldMarket),
481
+ heldMarketInfo.index
482
+ ),
483
+ liquidOwedWei: Interest.parToWei(
484
+ constants.dolomiteMargin.getAccountPar(constants.liquidAccount, owedMarket),
485
+ owedMarketInfo.index
486
+ ),
487
+ heldMarket: heldMarket,
488
+ owedMarket: owedMarket,
489
+ heldPrice: heldPrice,
490
+ owedPrice: owedPrice,
491
+ owedPriceAdj: owedPriceAdj
492
+ });
493
+ }
494
+
495
+ // ============ Operation-Construction Functions ============
496
+
497
+ function constructAccountsArray(
498
+ Constants memory constants,
499
+ Account.Info[] memory accountsForTrade
500
+ )
501
+ private
502
+ pure
503
+ returns (Account.Info[] memory)
504
+ {
505
+ Account.Info[] memory accounts = new Account.Info[](accountsForTrade.length + 1);
506
+ for (uint i = 0; i < accountsForTrade.length; i++) {
507
+ accounts[i] = accountsForTrade[i];
508
+ }
509
+ assert(
510
+ accounts[0].owner == constants.solidAccount.owner &&
511
+ accounts[0].number == constants.solidAccount.number
512
+ );
513
+
514
+ accounts[accounts.length - 1] = constants.liquidAccount;
515
+ return accounts;
516
+ }
517
+
518
+ function constructActionsArray(
519
+ Constants memory constants,
520
+ LiquidatorProxyWithAmmCache memory cache,
521
+ Account.Info[] memory accounts,
522
+ Actions.ActionArgs[] memory actionsForTrade
523
+ )
524
+ private
525
+ pure
526
+ returns (Actions.ActionArgs[] memory)
527
+ {
528
+ Actions.ActionArgs[] memory actions = new Actions.ActionArgs[](actionsForTrade.length + 1);
529
+
530
+ if (constants.expiry > 0) {
531
+ // First action is a trade for closing the expired account
532
+ // accountId is solidAccount; otherAccountId is liquidAccount
533
+ actions[0] = Actions.ActionArgs({
534
+ actionType: Actions.ActionType.Trade,
535
+ accountId: 0,
536
+ amount: Types.AssetAmount({
537
+ sign: true,
538
+ denomination: Types.AssetDenomination.Wei,
539
+ ref: Types.AssetReference.Delta,
540
+ value: cache.toLiquidate
541
+ }),
542
+ primaryMarketId: cache.owedMarket,
543
+ secondaryMarketId: cache.heldMarket,
544
+ otherAddress: address(constants.expiryProxy),
545
+ otherAccountId: accounts.length - 1,
546
+ data: abi.encode(cache.owedMarket, constants.expiry)
547
+ });
548
+ } else {
549
+ // First action is a liquidation
550
+ // accountId is solidAccount; otherAccountId is liquidAccount
551
+ actions[0] = Actions.ActionArgs({
552
+ actionType: Actions.ActionType.Liquidate,
553
+ accountId: 0,
554
+ amount: Types.AssetAmount({
555
+ sign: true,
556
+ denomination: Types.AssetDenomination.Wei,
557
+ ref: Types.AssetReference.Delta,
558
+ value: cache.toLiquidate
559
+ }),
560
+ primaryMarketId: cache.owedMarket,
561
+ secondaryMarketId: cache.heldMarket,
562
+ otherAddress: address(0),
563
+ otherAccountId: accounts.length - 1,
564
+ data: new bytes(0)
565
+ });
566
+ }
567
+
568
+ for (uint i = 0; i < actionsForTrade.length; i++) {
569
+ actions[i + 1] = actionsForTrade[i];
570
+ }
571
+
572
+ return actions;
573
+ }
574
+ }