@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,146 @@
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 { WETH9 } from "canonical-weth/contracts/WETH9.sol";
23
+ import { ReentrancyGuard } from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
24
+ import { Account } from "../../protocol/lib/Account.sol";
25
+ import { Actions } from "../../protocol/lib/Actions.sol";
26
+ import { Require } from "../../protocol/lib/Require.sol";
27
+ import { OnlyDolomiteMargin } from "../helpers/OnlyDolomiteMargin.sol";
28
+
29
+
30
+ /**
31
+ * @title PayableProxy
32
+ * @author dYdX
33
+ *
34
+ * Contract for wrapping/unwrapping ETH before/after interacting with DolomiteMargin
35
+ */
36
+ contract PayableProxy is OnlyDolomiteMargin, ReentrancyGuard {
37
+ // ============ Constants ============
38
+
39
+ bytes32 constant FILE = "PayableProxy";
40
+
41
+ // ============ Storage ============
42
+
43
+ WETH9 public WETH;
44
+
45
+ // ============ Constructor ============
46
+
47
+ constructor (
48
+ address dolomiteMargin,
49
+ address payable weth
50
+ )
51
+ public
52
+ OnlyDolomiteMargin(dolomiteMargin)
53
+ {
54
+ WETH = WETH9(weth);
55
+ WETH.approve(dolomiteMargin, uint256(-1));
56
+ }
57
+
58
+ // ============ Public Functions ============
59
+
60
+ /**
61
+ * Fallback function. Disallows ether to be sent to this contract without data except when
62
+ * unwrapping WETH.
63
+ */
64
+ function ()
65
+ external
66
+ payable
67
+ {
68
+ require( // coverage-disable-line
69
+ msg.sender == address(WETH),
70
+ "Cannot receive ETH"
71
+ );
72
+ }
73
+
74
+ function operate(
75
+ Account.Info[] memory accounts,
76
+ Actions.ActionArgs[] memory actions,
77
+ address payable sendEthTo
78
+ )
79
+ public
80
+ payable
81
+ nonReentrant
82
+ {
83
+ WETH9 weth = WETH;
84
+
85
+ // create WETH from ETH
86
+ if (msg.value != 0) {
87
+ weth.deposit.value(msg.value)();
88
+ }
89
+
90
+ // validate the input
91
+ for (uint256 i = 0; i < actions.length; i++) {
92
+ Actions.ActionArgs memory action = actions[i];
93
+
94
+ // Can only operate on accounts owned by msg.sender
95
+ address owner1 = accounts[action.accountId].owner;
96
+ Require.that(
97
+ owner1 == msg.sender,
98
+ FILE,
99
+ "Sender must be primary account",
100
+ owner1
101
+ );
102
+
103
+ // For a transfer both accounts must be owned by msg.sender
104
+ if (action.actionType == Actions.ActionType.Transfer) {
105
+ address owner2 = accounts[action.otherAccountId].owner;
106
+ Require.that(
107
+ owner2 == msg.sender,
108
+ FILE,
109
+ "Sender must be secondary account",
110
+ owner2
111
+ );
112
+ } else {
113
+ Require.that(
114
+ action.actionType != Actions.ActionType.Liquidate,
115
+ FILE,
116
+ "Cannot perform liquidations"
117
+ );
118
+ if (
119
+ action.actionType == Actions.ActionType.Trade &&
120
+ DOLOMITE_MARGIN.getIsAutoTraderSpecial(action.otherAddress)
121
+ ) {
122
+ Require.that(
123
+ DOLOMITE_MARGIN.getIsGlobalOperator(msg.sender),
124
+ FILE,
125
+ "Unpermissioned trade operator"
126
+ );
127
+ }
128
+ }
129
+ }
130
+
131
+ DOLOMITE_MARGIN.operate(accounts, actions);
132
+
133
+ // return all remaining WETH to the sendEthTo as ETH
134
+ uint256 remainingWeth = weth.balanceOf(address(this));
135
+ if (remainingWeth != 0) {
136
+ Require.that(
137
+ sendEthTo != address(0),
138
+ FILE,
139
+ "Must set sendEthTo"
140
+ );
141
+
142
+ weth.withdraw(remainingWeth);
143
+ sendEthTo.transfer(remainingWeth);
144
+ }
145
+ }
146
+ }
@@ -0,0 +1,463 @@
1
+ /*
2
+
3
+ Copyright 2021 Dolomite.
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 { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
23
+ import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
24
+ import { ReentrancyGuard } from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
25
+
26
+ import { Account } from "../../protocol/lib/Account.sol";
27
+ import { Actions } from "../../protocol/lib/Actions.sol";
28
+ import { Types } from "../../protocol/lib/Types.sol";
29
+ import { Require } from "../../protocol/lib/Require.sol";
30
+ import { IDolomiteMargin } from "../../protocol/interfaces/IDolomiteMargin.sol";
31
+ import { IERC20Detailed } from "../../protocol/interfaces/IERC20Detailed.sol";
32
+ import { IRecyclable } from "../../protocol/interfaces/IRecyclable.sol";
33
+
34
+ import { OnlyDolomiteMargin } from "../helpers/OnlyDolomiteMargin.sol";
35
+
36
+
37
+ import { IExpiry } from "../interfaces/IExpiry.sol";
38
+
39
+
40
+ /**
41
+ * @title RecyclableTokenProxy
42
+ * @author Dolomite
43
+ *
44
+ * Contract for wrapping around tokens to control how they are deposited into DolomiteMargin, to be combined with "market
45
+ * recycling" so "throwaway tokens" like options contracts that are represented as tokens can be used with the protocol
46
+ * and their market IDs can be safely reclaimed.
47
+ *
48
+ * This contract works by serving as a proxy account for a user. Meaning, a user deposits funds into DolomiteMargin using this
49
+ * contract's address as the `owner` and the user's address (converted to a uint) as the `number`. As a consequence and
50
+ * tradeoff, users can only have one margin position open per instance of this contract (per option token).
51
+ *
52
+ * The reason why this contract works well with a recycling strategy is because all usages of the instance's
53
+ * `marketId` are confined to this address as the `owner`. So, if the `marketId` is reused, it doesn't impact the user's
54
+ * balance, since a new instance of `RecyclableTokenProxy` will be deployed for the recycled marketId. Then, the new
55
+ * instance of `RecyclableTokenProxy` would serve as the new address for the user to interact with DolomiteMargin, masking/hiding
56
+ * the user's old (potentially) non-zero balance for that `marketId`. As a visualization, balances are mapped like so:
57
+ *
58
+ * `owner` --> `accountNumber` --> `marketId`
59
+ *
60
+ * `owner` corresponds with `address(this)`, `accountNumber` is the user's address, and `marketId` is recycled.
61
+ *
62
+ * Since `owner` constantly chances, the value of the mapping is able to reset, each time DolomiteMargin recycles a market.
63
+ *
64
+ * NOTE: Contracts that reference this token and implement IExchangeWrapper must set an allowance for this contract to
65
+ * spend `TOKEN` on the IExchangeWrapper implementor (TOKEN.approve(RecyclableTokenProxy, uint(-1)); call from
66
+ * IExchangeWrapper).
67
+ *
68
+ * Another note on balances: Part of the idea behind the implementation is to restrict usage of the recyclable token to
69
+ * only be held in `address(this)` owner address / Account.Info. The only time this marketId may reside in an `owner`
70
+ * that is NOT this contract is after a liquidation. This should not matter though, since the liquidator will withdraw
71
+ * the token to sell all of it for the owed collateral. So, after the liquidation transaction is over, the liquidator
72
+ * should have a zero balance anyway. Keeping the this token in the liquidators DolomiteMargin account, would cause catastrophic
73
+ * issues for the protocol when the `marketId` is recycled, since the liquidator's balance would be dirty upon reuse of
74
+ * the `marketId`. To mitigate this issue, a special liquidation contract should be created that purposely performs a
75
+ * withdrawal (down to zero) of this recyclable token's underlying `TOKEN`. Even if an implementing liquidation contract
76
+ * messes this up, there is a check done in `OperationImpl._verifyFinalState` that prevents this from happening.
77
+ */
78
+ contract RecyclableTokenProxy is IERC20Detailed, IRecyclable, OnlyDolomiteMargin, ReentrancyGuard {
79
+ using SafeERC20 for IERC20Detailed;
80
+
81
+ // ============ Constants ============
82
+
83
+ bytes32 internal constant FILE = "RecyclableTokenProxy";
84
+
85
+ // ============ Public Variables ============
86
+
87
+ IERC20Detailed public TOKEN;
88
+ IExpiry public EXPIRY;
89
+ uint256 public MARKET_ID;
90
+ bool public isRecycled;
91
+ mapping(address => mapping (uint256 => bool)) public userToAccountNumberHasWithdrawnAfterRecycle;
92
+
93
+ // ============ Constructor ============
94
+
95
+ constructor (
96
+ address dolomiteMargin,
97
+ address token,
98
+ address expiry,
99
+ uint maxExpirationTimestamp
100
+ )
101
+ public
102
+ OnlyDolomiteMargin(dolomiteMargin)
103
+ {
104
+ TOKEN = IERC20Detailed(token);
105
+ EXPIRY = IExpiry(expiry);
106
+ MAX_EXPIRATION_TIMESTAMP = maxExpirationTimestamp;
107
+ isRecycled = false;
108
+ }
109
+
110
+ // ============ Public Functions ============
111
+
112
+ function initialize() external onlyDolomiteMargin(msg.sender) {
113
+ Require.that(
114
+ MARKET_ID == 0,
115
+ FILE,
116
+ "already initialized"
117
+ );
118
+
119
+ MARKET_ID = DOLOMITE_MARGIN.getMarketIdByTokenAddress(address(this));
120
+
121
+ Require.that(
122
+ DOLOMITE_MARGIN.getMarketIsClosing(MARKET_ID),
123
+ FILE,
124
+ "market cannot allow borrowing"
125
+ );
126
+ // This statement is in this function because of an "InternalCompileError" error that occurs in the constructor
127
+ Require.that(
128
+ MAX_EXPIRATION_TIMESTAMP >= block.timestamp,
129
+ FILE,
130
+ "invalid expiration timestamp",
131
+ MAX_EXPIRATION_TIMESTAMP
132
+ );
133
+ }
134
+
135
+ function recycle() external onlyDolomiteMargin(msg.sender) {
136
+ Require.that(
137
+ DOLOMITE_MARGIN.getRecyclableMarkets(1)[0] == MARKET_ID,
138
+ FILE,
139
+ "not recyclable"
140
+ );
141
+
142
+ isRecycled = true;
143
+ }
144
+
145
+ function getAccountNumber(
146
+ Account.Info memory account
147
+ ) public pure returns (uint256) {
148
+ return _getAccountNumber(account.owner, account.number);
149
+ }
150
+
151
+ function getAccountPar(
152
+ Account.Info memory account
153
+ ) public view returns (Types.Par memory) {
154
+ if (userToAccountNumberHasWithdrawnAfterRecycle[account.owner][account.number]) {
155
+ return Types.zeroPar();
156
+ } else {
157
+ return DOLOMITE_MARGIN.getAccountParNoMarketCheck(
158
+ Account.Info(address(this), getAccountNumber(account)),
159
+ MARKET_ID
160
+ );
161
+ }
162
+ }
163
+
164
+ function depositIntoDolomiteMargin(uint accountNumber, uint amount) public nonReentrant {
165
+ Require.that(
166
+ !isRecycled,
167
+ FILE,
168
+ "cannot deposit when recycled"
169
+ );
170
+ Require.that(
171
+ !isExpired(),
172
+ FILE,
173
+ "market is expired",
174
+ MAX_EXPIRATION_TIMESTAMP
175
+ );
176
+
177
+ TOKEN.safeTransferFrom(msg.sender, address(this), amount);
178
+
179
+ Account.Info[] memory accounts = new Account.Info[](1);
180
+ accounts[0] = Account.Info(address(this), _getAccountNumber(msg.sender, accountNumber));
181
+
182
+ Actions.ActionArgs[] memory actions = new Actions.ActionArgs[](1);
183
+ actions[0] = Actions.ActionArgs({
184
+ actionType: Actions.ActionType.Deposit,
185
+ accountId: 0,
186
+ // solium-disable-next-line arg-overflow
187
+ amount: Types.AssetAmount(true, Types.AssetDenomination.Wei, Types.AssetReference.Delta, amount),
188
+ primaryMarketId: MARKET_ID,
189
+ secondaryMarketId: uint(-1),
190
+ otherAddress: address(this),
191
+ otherAccountId: uint(-1),
192
+ data: bytes("")
193
+ });
194
+
195
+ DOLOMITE_MARGIN.operate(accounts, actions);
196
+ }
197
+
198
+ function withdrawFromDolomiteMargin(uint accountNumber, uint amount) public nonReentrant {
199
+ Require.that(
200
+ !isRecycled,
201
+ FILE,
202
+ "cannot withdraw when recycled"
203
+ );
204
+
205
+ Account.Info[] memory accounts = new Account.Info[](1);
206
+ accounts[0] = Account.Info(address(this), _getAccountNumber(msg.sender, accountNumber));
207
+
208
+ Actions.ActionArgs[] memory actions = new Actions.ActionArgs[](1);
209
+ actions[0] = Actions.ActionArgs({
210
+ actionType: Actions.ActionType.Withdraw,
211
+ accountId: 0,
212
+ // solium-disable-next-line arg-overflow
213
+ amount: Types.AssetAmount(false, Types.AssetDenomination.Wei, Types.AssetReference.Delta, amount),
214
+ primaryMarketId: MARKET_ID,
215
+ secondaryMarketId: uint(-1),
216
+ otherAddress: msg.sender,
217
+ otherAccountId: uint(-1),
218
+ data: bytes("")
219
+ });
220
+
221
+ DOLOMITE_MARGIN.operate(accounts, actions);
222
+ }
223
+
224
+ function withdrawAfterRecycle(uint accountNumber) public {
225
+ Require.that(
226
+ isRecycled,
227
+ FILE,
228
+ "not recycled yet"
229
+ );
230
+ Require.that(
231
+ !userToAccountNumberHasWithdrawnAfterRecycle[msg.sender][accountNumber],
232
+ FILE,
233
+ "user already withdrew"
234
+ );
235
+ userToAccountNumberHasWithdrawnAfterRecycle[msg.sender][accountNumber] = true;
236
+ TOKEN.safeTransfer(
237
+ msg.sender,
238
+ DOLOMITE_MARGIN.getAccountParNoMarketCheck(
239
+ Account.Info(address(this), _getAccountNumber(msg.sender, accountNumber)),
240
+ MARKET_ID
241
+ ).value
242
+ );
243
+ }
244
+
245
+ function trade(
246
+ uint accountNumber,
247
+ Types.AssetAmount memory supplyAmount, // equivalent to amounts[amounts.length - 1]
248
+ address borrowToken,
249
+ Types.AssetAmount memory borrowAmount,
250
+ address exchangeWrapper,
251
+ uint expirationTimestamp,
252
+ bool isOpen,
253
+ bytes memory tradeData
254
+ ) public {
255
+ Require.that(
256
+ !isRecycled,
257
+ FILE,
258
+ "cannot trade when recycled"
259
+ );
260
+ Require.that(
261
+ !isExpired(),
262
+ FILE,
263
+ "market is expired",
264
+ MAX_EXPIRATION_TIMESTAMP
265
+ );
266
+ Require.that(
267
+ expirationTimestamp > block.timestamp,
268
+ FILE,
269
+ "expiration timestamp too low",
270
+ expirationTimestamp
271
+ );
272
+ Require.that(
273
+ expirationTimestamp <= MAX_EXPIRATION_TIMESTAMP,
274
+ FILE,
275
+ "expiration timestamp too high",
276
+ expirationTimestamp
277
+ );
278
+
279
+ uint marketId = MARKET_ID;
280
+ uint borrowMarketId = DOLOMITE_MARGIN.getMarketIdByTokenAddress(borrowToken);
281
+
282
+ Account.Info[] memory accounts = new Account.Info[](1);
283
+ accounts[0] = Account.Info(address(this), _getAccountNumber(msg.sender, accountNumber));
284
+
285
+ Actions.ActionArgs[] memory actions = new Actions.ActionArgs[](2);
286
+ actions[0] = Actions.ActionArgs({
287
+ actionType: Actions.ActionType.Sell,
288
+ accountId: 0,
289
+ amount: isOpen ? borrowAmount : supplyAmount,
290
+ primaryMarketId: isOpen ? borrowMarketId : marketId,
291
+ secondaryMarketId: isOpen ? marketId : borrowMarketId,
292
+ otherAddress: exchangeWrapper,
293
+ otherAccountId: 0,
294
+ data: tradeData
295
+ });
296
+
297
+ actions[1] = Actions.ActionArgs({
298
+ actionType : Actions.ActionType.Call,
299
+ accountId : 0,
300
+ // solium-disable-next-line arg-overflow
301
+ amount : Types.AssetAmount(false, Types.AssetDenomination.Wei, Types.AssetReference.Delta, 0),
302
+ primaryMarketId : 0,
303
+ secondaryMarketId : 0,
304
+ otherAddress : address(EXPIRY),
305
+ otherAccountId : 0,
306
+ data : abi.encode(
307
+ IExpiry.CallFunctionType.SetExpiry,
308
+ _getExpiryArgs(accounts[0], borrowMarketId, isOpen ? expirationTimestamp : 0)
309
+ )
310
+ });
311
+
312
+ DOLOMITE_MARGIN.operate(accounts, actions);
313
+ }
314
+
315
+ // ============ ERC20 Functions ============
316
+
317
+ function name() public view returns (string memory) {
318
+ (bool isSuccess, bytes memory data) = address(TOKEN).staticcall(abi.encodePacked(TOKEN.name.selector));
319
+ if (isSuccess && data.length > 0) {
320
+ return string(abi.encodePacked("Recyclable: ", abi.decode(data, (string))));
321
+ } else {
322
+ return "Recyclable: Dolomite Token";
323
+ }
324
+ }
325
+
326
+ function symbol() public view returns (string memory) {
327
+ (bool isSuccess, bytes memory data) = address(TOKEN).staticcall(abi.encodePacked(TOKEN.symbol.selector));
328
+ if (isSuccess && data.length > 0) {
329
+ return string(abi.encodePacked("r", abi.decode(data, (string))));
330
+ } else {
331
+ return "rDOLO_TOKEN";
332
+ }
333
+ }
334
+
335
+ function decimals() public view returns (uint8) {
336
+ (bool isSuccess, bytes memory data) = address(TOKEN).staticcall(abi.encodePacked(TOKEN.decimals.selector));
337
+ if (isSuccess && data.length > 0) {
338
+ return abi.decode(data, (uint8));
339
+ } else {
340
+ return 18;
341
+ }
342
+ }
343
+
344
+ function totalSupply() public view returns (uint256) {
345
+ return TOKEN.totalSupply();
346
+ }
347
+
348
+ function balanceOf(address account) public view returns (uint256) {
349
+ if (account == address(DOLOMITE_MARGIN)) {
350
+ // The effective balance of DolomiteMargin is the balance held of the underlying token in this contract
351
+ return TOKEN.balanceOf(address(this));
352
+ }
353
+
354
+ uint accountNumber = 0;
355
+ if (userToAccountNumberHasWithdrawnAfterRecycle[account][accountNumber]) {
356
+ return 0;
357
+ } else {
358
+ return getAccountPar(Account.Info(account, accountNumber)).value;
359
+ }
360
+ }
361
+
362
+ function transfer(address recipient, uint256 amount) public onlyDolomiteMargin(msg.sender) returns (bool) {
363
+ // This condition fails when the market is recycled but DolomiteMargin attempts to call this contract still
364
+ Require.that(
365
+ DOLOMITE_MARGIN.getMarketTokenAddress(MARKET_ID) == address(this),
366
+ FILE,
367
+ "invalid state"
368
+ );
369
+ Require.that(
370
+ !isRecycled,
371
+ FILE,
372
+ "cannot transfer while recycled"
373
+ );
374
+
375
+ TOKEN.safeTransfer(recipient, amount);
376
+ emit Transfer(msg.sender, recipient, amount);
377
+ return true;
378
+ }
379
+
380
+ function allowance(address, address) public view returns (uint256) {
381
+ return 0;
382
+ }
383
+
384
+ function approve(address, uint256) public returns (bool) {
385
+ return false;
386
+ }
387
+
388
+ function transferFrom(
389
+ address from,
390
+ address to,
391
+ uint256 amount
392
+ ) public onlyDolomiteMargin(msg.sender) returns (bool) {
393
+ // transferFrom should always send tokens to DOLOMITE_MARGIN
394
+ Require.that(
395
+ to == address(msg.sender), // msg.sender eq DOLOMITE_MARGIN
396
+ FILE,
397
+ "invalid recipient"
398
+ );
399
+ Require.that(
400
+ !isRecycled,
401
+ FILE,
402
+ "cannot transfer while recycled"
403
+ );
404
+ // This condition fails when the market is recycled but DolomiteMargin attempts to call this contract anyway
405
+ Require.that(
406
+ DOLOMITE_MARGIN.getMarketTokenAddress(MARKET_ID) == address(this),
407
+ FILE,
408
+ "invalid state"
409
+ );
410
+
411
+ if (from == address(this)) {
412
+ // token is being transferred from here to DolomiteMargin, for a deposit. The market's total par was already updated
413
+ // before the call to `transferFrom`. Make sure enough was transferred in.
414
+ // This implementation allows the user to "steal" funds from users that blindly send TOKEN into this
415
+ // contract, without calling properly calling the `deposit` function to set their balances.
416
+ Require.that(
417
+ TOKEN.balanceOf(address(this)) >= DOLOMITE_MARGIN.getMarketTotalPar(MARKET_ID).supply,
418
+ FILE,
419
+ "insufficient balance for deposit"
420
+ );
421
+ emit Transfer(address(this), to, amount);
422
+ } else {
423
+ // TOKEN is being traded via IExchangeWrapper, transfer the tokens into this contract
424
+ TOKEN.safeTransferFrom(from, address(this), amount);
425
+
426
+ // The market's total par was already updated before the call to `transferFrom`. Make sure enough was
427
+ // transferred in. This implementation allows the user to "steal" funds from users that blindly send TOKEN
428
+ // into this contract, without calling properly calling the `deposit` function to set their balances.
429
+ Require.that(
430
+ TOKEN.balanceOf(address(this)) >= DOLOMITE_MARGIN.getMarketTotalPar(MARKET_ID).supply,
431
+ FILE,
432
+ "insufficient balance for deposit"
433
+ );
434
+
435
+ // this transfer event is technically incorrect since the tokens are really sent from address(this) to
436
+ // recipient, not `sender`. However, we'll let it go.
437
+ emit Transfer(from, to, amount);
438
+ }
439
+ return true;
440
+ }
441
+
442
+ // ============ Private Functions ============
443
+
444
+ function _getAccountNumber(address owner, uint number) private pure returns (uint256) {
445
+ return uint(keccak256(abi.encode(owner, number)));
446
+ }
447
+
448
+ function _getExpiryArgs(
449
+ Account.Info memory account,
450
+ uint marketId,
451
+ uint expirationTimestamp
452
+ ) private view returns (IExpiry.SetExpiryArg[] memory) {
453
+ IExpiry.SetExpiryArg[] memory expiryArgs = new IExpiry.SetExpiryArg[](1);
454
+ expiryArgs[0] = IExpiry.SetExpiryArg({
455
+ account : account,
456
+ marketId : marketId,
457
+ timeDelta : expirationTimestamp == 0 ? 0 : uint32(expirationTimestamp - block.timestamp),
458
+ forceUpdate : true
459
+ });
460
+ return expiryArgs;
461
+ }
462
+
463
+ }