@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.
- package/build/contracts/Account.json +11 -11
- package/build/contracts/Actions.json +11 -11
- package/build/contracts/Address.json +1 -1
- package/build/contracts/Admin.json +23 -23
- package/build/contracts/AdminImpl.json +35 -307
- package/build/contracts/AdvancedMath.json +7 -7
- package/build/contracts/AmmRebalancerProxy.json +577 -621
- package/build/contracts/AmmRebalancerProxyV1.json +78 -32
- package/build/contracts/Babylonian.json +1 -1
- package/build/contracts/Bits.json +9 -9
- package/build/contracts/Cache.json +13 -13
- package/build/contracts/ChainlinkPriceOracleV1.json +16 -16
- package/build/contracts/Context.json +1 -1
- package/build/contracts/CustomTestToken.json +9 -9
- package/build/contracts/Decimal.json +9 -9
- package/build/contracts/DelayedMultiSig.json +9 -9
- package/build/contracts/DolomiteAmmERC20.json +11 -11
- package/build/contracts/DolomiteAmmFactory.json +14 -52
- package/build/contracts/DolomiteAmmLibrary.json +24 -24
- package/build/contracts/DolomiteAmmPair.json +27 -27
- package/build/contracts/DolomiteAmmRouterProxy.json +30 -363
- package/build/contracts/DolomiteMargin.json +21 -67
- package/build/contracts/DoubleExponentInterestSetter.json +15 -21
- package/build/contracts/EnumerableSet.json +7 -7
- package/build/contracts/ErroringOmiseToken.json +9 -9
- package/build/contracts/ErroringToken.json +10 -10
- package/build/contracts/Events.json +17 -17
- package/build/contracts/ExcessivelySafeCall.json +35 -35
- package/build/contracts/Exchange.json +15 -15
- package/build/contracts/Expiry.json +36 -36
- package/build/contracts/Getters.json +33 -33
- package/build/contracts/IAutoTrader.json +9 -9
- package/build/contracts/ICallee.json +7 -7
- package/build/contracts/IChainlinkAggregator.json +5 -5
- package/build/contracts/IDolomiteAmmERC20.json +5 -5
- package/build/contracts/IDolomiteAmmFactory.json +9 -9
- package/build/contracts/IDolomiteAmmPair.json +9 -9
- package/build/contracts/IDolomiteMargin.json +23 -23
- package/build/contracts/IERC20.json +1 -1
- package/build/contracts/IERC20Detailed.json +5 -5
- package/build/contracts/IExchangeWrapper.json +5 -5
- package/build/contracts/IExpiry.json +9 -9
- package/build/contracts/IInterestSetter.json +7 -7
- package/build/contracts/ILiquidationCallback.json +7 -7
- package/build/contracts/IMakerOracle.json +5 -5
- package/build/contracts/IOasisDex.json +5 -5
- package/build/contracts/IPriceOracle.json +7 -7
- package/build/contracts/IRecyclable.json +9 -9
- package/build/contracts/ITransferProxy.json +5 -5
- package/build/contracts/IUniswapV2Callee.json +5 -5
- package/build/contracts/IUniswapV2Factory.json +5 -5
- package/build/contracts/IUniswapV2Pair.json +5 -5
- package/build/contracts/IUniswapV2Router.json +5 -5
- package/build/contracts/IWETH.json +5 -5
- package/build/contracts/Interest.json +15 -15
- package/build/contracts/LiquidateOrVaporizeImpl.json +32 -83
- package/build/contracts/LiquidatorProxyHelper.json +23 -23
- package/build/contracts/LiquidatorProxyV1.json +30 -36
- package/build/contracts/LiquidatorProxyV1WithAmm.json +49 -105
- package/build/contracts/Math.json +1 -1
- package/build/contracts/Migrations.json +8 -14
- package/build/contracts/Monetary.json +7 -7
- package/build/contracts/MultiCall.json +8 -14
- package/build/contracts/MultiSig.json +7 -7
- package/build/contracts/OmiseToken.json +8 -8
- package/build/contracts/OnlyDolomiteMargin.json +11 -11
- package/build/contracts/Operation.json +15 -15
- package/build/contracts/OperationImpl.json +39 -47
- package/build/contracts/Ownable.json +1 -1
- package/build/contracts/PartiallyDelayedMultiSig.json +9 -9
- package/build/contracts/PayableProxy.json +17 -23
- package/build/contracts/Permission.json +11 -11
- package/build/contracts/PolynomialInterestSetter.json +13 -13
- package/build/contracts/RecyclableTokenProxy.json +25 -25
- package/build/contracts/ReentrancyGuard.json +1 -1
- package/build/contracts/Require.json +7 -7
- package/build/contracts/SafeERC20.json +1 -1
- package/build/contracts/SafeETH.json +7 -7
- package/build/contracts/SafeLiquidationCallback.json +17 -17
- package/build/contracts/SafeMath.json +1 -1
- package/build/contracts/SignedOperationProxy.json +21 -104
- package/build/contracts/SimpleFeeOwner.json +18 -63
- package/build/contracts/State.json +9 -9
- package/build/contracts/Storage.json +37 -37
- package/build/contracts/TestAmmRebalancerProxy.json +32 -58
- package/build/contracts/TestAutoTrader.json +22 -22
- package/build/contracts/TestBtcUsdChainlinkAggregator.json +10 -10
- package/build/contracts/TestCallee.json +19 -19
- package/build/contracts/TestChainlinkPriceOracleV1.json +10 -94
- package/build/contracts/TestCounter.json +7 -7
- package/build/contracts/TestDaiUsdChainlinkAggregator.json +11 -11
- package/build/contracts/TestDolomiteMargin.json +22 -22
- package/build/contracts/TestDoubleExponentInterestSetter.json +10 -10
- package/build/contracts/TestEthUsdChainlinkAggregator.json +11 -11
- package/build/contracts/TestExchangeWrapper.json +28 -28
- package/build/contracts/TestInterestSetter.json +13 -13
- package/build/contracts/TestLib.json +22 -22
- package/build/contracts/TestLinkUsdChainlinkAggregator.json +11 -11
- package/build/contracts/TestLiquidateCallback.json +719 -719
- package/build/contracts/TestLiquidationCallback.json +13 -13
- package/build/contracts/TestLrcEthChainlinkAggregator.json +11 -11
- package/build/contracts/TestMakerOracle.json +10 -10
- package/build/contracts/TestMaticUsdChainlinkAggregator.json +10 -10
- package/build/contracts/TestOasisDex.json +12 -12
- package/build/contracts/TestOperationImpl.json +14 -14
- package/build/contracts/TestPolynomialInterestSetter.json +10 -10
- package/build/contracts/TestPriceAggregator.json +9 -9
- package/build/contracts/TestPriceOracle.json +13 -13
- package/build/contracts/TestRecyclableToken.json +9 -9
- package/build/contracts/TestSimpleCallee.json +15 -15
- package/build/contracts/TestToken.json +9 -9
- package/build/contracts/TestTrader.json +17 -17
- package/build/contracts/TestUniswapAmmRebalancerProxy.json +16 -16
- package/build/contracts/TestUsdcUsdChainlinkAggregator.json +11 -11
- package/build/contracts/TestWETH.json +9 -9
- package/build/contracts/Time.json +9 -9
- package/build/contracts/Token.json +9 -9
- package/build/contracts/TokenA.json +10 -10
- package/build/contracts/TokenB.json +10 -10
- package/build/contracts/TokenC.json +10 -10
- package/build/contracts/TokenD.json +10 -10
- package/build/contracts/TokenE.json +10 -10
- package/build/contracts/TokenF.json +10 -10
- package/build/contracts/TransferProxy.json +22 -28
- package/build/contracts/TypedSignature.json +9 -9
- package/build/contracts/Types.json +9 -9
- package/build/contracts/UQ112x112.json +7 -7
- package/build/contracts/UniswapV2ERC20.json +7 -7
- package/build/contracts/UniswapV2Factory.json +12 -12
- package/build/contracts/UniswapV2Library.json +20 -20
- package/build/contracts/UniswapV2Pair.json +17 -17
- package/build/contracts/UniswapV2Router02.json +16 -16
- package/build/contracts/WETH9.json +1 -1
- package/contracts/external/amm/DolomiteAmmERC20.sol +135 -0
- package/contracts/external/amm/DolomiteAmmFactory.sol +122 -0
- package/contracts/external/amm/DolomiteAmmPair.sol +573 -0
- package/contracts/external/amm/SimpleFeeOwner.sol +107 -0
- package/contracts/external/helpers/LiquidatorProxyHelper.sol +252 -0
- package/contracts/external/helpers/OnlyDolomiteMargin.sol +63 -0
- package/contracts/external/interestsetters/DoubleExponentInterestSetter.sol +212 -0
- package/contracts/external/interestsetters/PolynomialInterestSetter.sol +205 -0
- package/contracts/external/interfaces/IChainlinkAggregator.sol +33 -0
- package/contracts/external/interfaces/IDolomiteAmmERC20.sol +52 -0
- package/contracts/external/interfaces/IDolomiteAmmFactory.sol +42 -0
- package/contracts/external/interfaces/IDolomiteAmmPair.sol +116 -0
- package/contracts/external/interfaces/IExpiry.sol +70 -0
- package/contracts/external/interfaces/IMakerOracle.sol +52 -0
- package/contracts/external/interfaces/IOasisDex.sol +326 -0
- package/contracts/external/interfaces/ITransferProxy.sol +53 -0
- package/contracts/external/interfaces/IUniswapV2Router.sol +134 -0
- package/contracts/external/lib/AdvancedMath.sol +23 -0
- package/contracts/external/lib/DolomiteAmmLibrary.sol +323 -0
- package/contracts/external/lib/TypedSignature.sol +120 -0
- package/contracts/external/lib/UQ112x112.sol +22 -0
- package/contracts/external/multisig/DelayedMultiSig.sol +206 -0
- package/contracts/external/multisig/MultiSig.sol +571 -0
- package/contracts/external/multisig/PartiallyDelayedMultiSig.sol +174 -0
- package/contracts/external/oracles/ChainlinkPriceOracleV1.sol +197 -0
- package/contracts/external/oracles/TestChainlinkPriceOracleV1.sol +98 -0
- package/contracts/external/proxies/AmmRebalancerProxyV1.sol +465 -0
- package/contracts/external/proxies/DolomiteAmmRouterProxy.sol +877 -0
- package/contracts/external/proxies/LiquidatorProxyV1.sol +507 -0
- package/contracts/external/proxies/LiquidatorProxyV1WithAmm.sol +574 -0
- package/contracts/external/proxies/PayableProxy.sol +146 -0
- package/contracts/external/proxies/RecyclableTokenProxy.sol +463 -0
- package/contracts/external/proxies/SignedOperationProxy.sol +553 -0
- package/contracts/external/proxies/TransferProxy.sol +207 -0
- package/contracts/external/traders/Expiry.sol +532 -0
- package/contracts/external/uniswap-v2/UniswapV2ERC20.sol +118 -0
- package/contracts/external/uniswap-v2/UniswapV2Factory.sol +67 -0
- package/contracts/external/uniswap-v2/UniswapV2Pair.sol +283 -0
- package/contracts/external/uniswap-v2/UniswapV2Router02.sol +566 -0
- package/contracts/external/uniswap-v2/interfaces/IUniswapV2Callee.sol +13 -0
- package/contracts/external/uniswap-v2/interfaces/IUniswapV2Factory.sol +18 -0
- package/contracts/external/uniswap-v2/interfaces/IUniswapV2Pair.sol +67 -0
- package/contracts/external/uniswap-v2/interfaces/IWETH.sol +7 -0
- package/contracts/external/uniswap-v2/libraries/SafeETH.sol +29 -0
- package/contracts/external/uniswap-v2/libraries/UniswapV2Library.sol +117 -0
- package/contracts/external/utils/MultiCall.sol +95 -0
- package/contracts/protocol/impl/artifacts/OperationImpl.json +80 -0
- package/contracts/protocol/impl/artifacts/OperationImpl_metadata.json +193 -0
- package/dist/build/published_contracts/AdminImpl.json +2 -2
- package/dist/build/published_contracts/AmmRebalancerProxyV1.json +5 -0
- package/dist/build/published_contracts/ChainlinkPriceOracleV1.json +1 -1
- package/dist/build/published_contracts/DolomiteAmmFactory.json +1 -1
- package/dist/build/published_contracts/DolomiteAmmRouterProxy.json +1 -1
- package/dist/build/published_contracts/DoubleExponentInterestSetter.json +2 -2
- package/dist/build/published_contracts/Expiry.json +1 -1
- package/dist/build/published_contracts/LiquidatorProxyV1.json +1 -1
- package/dist/build/published_contracts/LiquidatorProxyV1WithAmm.json +1 -1
- package/dist/build/published_contracts/MultiCall.json +1 -1
- package/dist/build/published_contracts/PayableProxy.json +2 -2
- package/dist/build/published_contracts/SignedOperationProxy.json +2 -2
- package/dist/build/published_contracts/SimpleFeeOwner.json +1 -1
- package/dist/build/published_contracts/TestUniswapAmmRebalancerProxy.json +140 -0
- package/dist/build/published_contracts/TransferProxy.json +1 -1
- package/dist/src/lib/Contracts.d.ts +3 -1
- package/dist/src/lib/Contracts.js +7 -3
- package/dist/src/lib/Contracts.js.map +1 -1
- 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
|
+
}
|