Overview
ETH Balance
ETH Value
$0.00Latest 25 from a total of 1,122 transactions
| Transaction Hash |
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
| Liquidate | 243298589 | 528 days ago | IN | 0 ETH | 0.00005178 | ||||
| Liquidate | 242279453 | 531 days ago | IN | 0 ETH | 0.00006049 | ||||
| Liquidate | 242128125 | 531 days ago | IN | 0 ETH | 0.00019184 | ||||
| Liquidate | 241157119 | 534 days ago | IN | 0 ETH | 0.00007546 | ||||
| Liquidate | 240443129 | 536 days ago | IN | 0 ETH | 0.00022725 | ||||
| Liquidate | 240095200 | 537 days ago | IN | 0 ETH | 0.00007218 | ||||
| Liquidate | 240095027 | 537 days ago | IN | 0 ETH | 0.00006426 | ||||
| Liquidate | 240095013 | 537 days ago | IN | 0 ETH | 0.00005404 | ||||
| Liquidate | 240094999 | 537 days ago | IN | 0 ETH | 0.00005448 | ||||
| Liquidate | 240094985 | 537 days ago | IN | 0 ETH | 0.00005523 | ||||
| Liquidate | 240094972 | 537 days ago | IN | 0 ETH | 0.0000537 | ||||
| Liquidate | 240094958 | 537 days ago | IN | 0 ETH | 0.00005493 | ||||
| Liquidate | 240094824 | 537 days ago | IN | 0 ETH | 0.00005925 | ||||
| Liquidate | 240094809 | 537 days ago | IN | 0 ETH | 0.00007848 | ||||
| Liquidate | 240094793 | 537 days ago | IN | 0 ETH | 0.00006055 | ||||
| Liquidate | 240094780 | 537 days ago | IN | 0 ETH | 0.00006097 | ||||
| Liquidate | 240094766 | 537 days ago | IN | 0 ETH | 0.0000622 | ||||
| Liquidate | 240094752 | 537 days ago | IN | 0 ETH | 0.00006697 | ||||
| Liquidate | 240094738 | 537 days ago | IN | 0 ETH | 0.00006306 | ||||
| Liquidate | 240094722 | 537 days ago | IN | 0 ETH | 0.00006418 | ||||
| Liquidate | 240094708 | 537 days ago | IN | 0 ETH | 0.00006232 | ||||
| Liquidate | 240094694 | 537 days ago | IN | 0 ETH | 0.00006441 | ||||
| Liquidate | 240094666 | 537 days ago | IN | 0 ETH | 0.00008662 | ||||
| Liquidate | 240094651 | 537 days ago | IN | 0 ETH | 0.00005776 | ||||
| Liquidate | 240094638 | 537 days ago | IN | 0 ETH | 0.00005922 |
Cross-Chain Transactions
Contract Source Code (Solidity Standard Json-Input format)
/*
Copyright 2022 Dolomite.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.7;
pragma experimental ABIEncoderV2;
import { ReentrancyGuard } from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import { IDolomiteMargin } from "../../protocol/interfaces/IDolomiteMargin.sol";
import { Account } from "../../protocol/lib/Account.sol";
import { Actions } from "../../protocol/lib/Actions.sol";
import { Require } from "../../protocol/lib/Require.sol";
import { Types } from "../../protocol/lib/Types.sol";
import { GenericTraderProxyBase } from "../helpers/GenericTraderProxyBase.sol";
import { HasLiquidatorRegistry } from "../helpers/HasLiquidatorRegistry.sol";
import { LiquidatorProxyBase } from "../helpers/LiquidatorProxyBase.sol";
import { IEventEmitterRegistry } from "../interfaces/IEventEmitterRegistry.sol";
import { IExpiry } from "../interfaces/IExpiry.sol";
import { IIsolationModeUnwrapperTrader } from "../interfaces/IIsolationModeUnwrapperTrader.sol";
import { IIsolationModeWrapperTrader } from "../interfaces/IIsolationModeWrapperTrader.sol";
import { AccountActionLib } from "../lib/AccountActionLib.sol";
import { LiquidatorProxyV2WithExternalLiquidity } from "./LiquidatorProxyV2WithExternalLiquidity.sol";
/**
* @title LiquidatorProxyV4WithGenericTrader
* @author Dolomite
*
* Contract for liquidating accounts in DolomiteMargin using generic traders. This contract should presumably work with
* any liquidation strategy due to its generic implementation. As such, tremendous care should be taken to ensure that
* the `traders` array passed to the `liquidate` function is correct and will not result in any unexpected behavior
* for special assets like IsolationMode tokens.
*/
contract LiquidatorProxyV4WithGenericTrader is
HasLiquidatorRegistry,
LiquidatorProxyBase,
GenericTraderProxyBase,
ReentrancyGuard
{
// ============ Constants ============
bytes32 private constant FILE = "LiquidatorProxyV4";
uint256 private constant LIQUID_ACCOUNT_ID = 2;
// ============ Storage ============
IExpiry public EXPIRY;
IDolomiteMargin public DOLOMITE_MARGIN;
// ============ Constructor ============
constructor (
address _expiryProxy,
address _dolomiteMargin,
address _liquidatorAssetRegistry
)
public
HasLiquidatorRegistry(
_liquidatorAssetRegistry
)
{
EXPIRY = IExpiry(_expiryProxy);
DOLOMITE_MARGIN = IDolomiteMargin(_dolomiteMargin);
}
// ============ External Functions ============
function liquidate(
Account.Info memory _solidAccount,
Account.Info memory _liquidAccount,
uint256[] memory _marketIdsPath,
uint256 _inputAmountWei,
uint256 _minOutputAmountWei,
TraderParam[] memory _tradersPath,
Account.Info[] memory _makerAccounts,
uint256 _expiry
)
public
nonReentrant
{
GenericTraderProxyCache memory genericCache = GenericTraderProxyCache({
dolomiteMargin: DOLOMITE_MARGIN,
eventEmitterRegistry: IEventEmitterRegistry(address(0)),
// unused for this function
isMarginDeposit: false,
// unused for this function
otherAccountNumber: 0,
// traders go right after the liquid account ("other account")
traderAccountStartIndex: LIQUID_ACCOUNT_ID + 1,
actionsCursor: 0,
// unused for this function
inputBalanceWeiBeforeOperate: Types.zeroWei(),
// unused for this function
outputBalanceWeiBeforeOperate: Types.zeroWei(),
// unused for this function
transferBalanceWeiBeforeOperate: Types.zeroWei()
});
_validateMarketIdPath(_marketIdsPath);
_validateAmountWeis(_inputAmountWei, _minOutputAmountWei);
_validateTraderParams(
genericCache,
_marketIdsPath,
_makerAccounts,
_tradersPath
);
_validateInputAmountAndInputMarketForIsolationMode(_tradersPath[0], _inputAmountWei);
// put all values that will not change into a single struct
LiquidatorProxyConstants memory constants;
constants.dolomiteMargin = genericCache.dolomiteMargin;
constants.solidAccount = _solidAccount;
constants.liquidAccount = _liquidAccount;
constants.heldMarket = _marketIdsPath[0];
constants.owedMarket = _marketIdsPath[_marketIdsPath.length - 1];
_checkConstants(constants, _expiry);
_validateAssetForLiquidation(constants.heldMarket);
_validateAssetForLiquidation(constants.owedMarket);
constants.liquidMarkets = constants.dolomiteMargin.getAccountMarketsWithBalances(constants.liquidAccount);
constants.markets = _getMarketInfos(
constants.dolomiteMargin,
constants.dolomiteMargin.getAccountMarketsWithBalances(_solidAccount),
constants.liquidMarkets
);
constants.expiryProxy = _expiry > 0 ? EXPIRY: IExpiry(address(0)); // don't read EXPIRY; it's not needed
constants.expiry = uint32(_expiry);
LiquidatorProxyCache memory liquidatorCache = _initializeCache(constants);
// validate the msg.sender and that the liquidAccount can be liquidated
_checkBasicRequirements(constants);
// get the max liquidation amount
_calculateAndSetMaxLiquidationAmount(liquidatorCache);
_minOutputAmountWei = _calculateAndSetActualLiquidationAmount(_minOutputAmountWei, liquidatorCache);
Account.Info[] memory accounts = _getAccounts(
genericCache,
_makerAccounts,
_solidAccount.owner,
_solidAccount.number
);
// the call to _getAccounts leaves accounts[LIQUID_ACCOUNT_ID] null because it fills in the traders starting at
// the `traderAccountCursor` index
accounts[LIQUID_ACCOUNT_ID] = _liquidAccount;
_validateZapAccount(genericCache, accounts[ZAP_ACCOUNT_ID], _marketIdsPath);
Actions.ActionArgs[] memory actions = new Actions.ActionArgs[](
/* liquidationActionsLength = */ 1 + _getActionsLengthForTraderParams(_tradersPath)
);
_appendLiquidationAction(
actions,
constants,
liquidatorCache,
genericCache
);
_appendTraderActions(
accounts,
actions,
genericCache,
_marketIdsPath,
_inputAmountWei,
_minOutputAmountWei,
_tradersPath
);
genericCache.dolomiteMargin.operate(accounts, actions);
}
// ============ Internal Functions ============
function _validateInputAmountAndInputMarketForIsolationMode(
TraderParam memory _param,
uint256 _inputAmountWei
) internal pure {
if (_isUnwrapperTraderType(_param.traderType) || _isWrapperTraderType(_param.traderType)) {
// For liquidations, the asset amount must match the amount of collateral transferred from liquid account
// to solid account. This is done via always selling the max amount of held collateral.
Require.that(
_inputAmountWei == uint256(-1),
FILE,
"Invalid amount for IsolationMode"
);
}
}
function _appendLiquidationAction(
Actions.ActionArgs[] memory _actions,
LiquidatorProxyConstants memory _constants,
LiquidatorProxyCache memory _liquidatorCache,
GenericTraderProxyCache memory _genericCache
)
internal
pure
{
// solidAccountId is always at index 0, liquidAccountId is always at index 1
if (_constants.expiry > 0) {
_actions[_genericCache.actionsCursor++] = AccountActionLib.encodeExpiryLiquidateAction(
TRADE_ACCOUNT_ID,
LIQUID_ACCOUNT_ID,
_constants.owedMarket,
_constants.heldMarket,
address(_constants.expiryProxy),
_constants.expiry,
_liquidatorCache.solidHeldUpdateWithReward,
_liquidatorCache.owedWeiToLiquidate,
_liquidatorCache.flipMarketsForExpiration
);
} else {
_actions[_genericCache.actionsCursor++] = AccountActionLib.encodeLiquidateAction(
TRADE_ACCOUNT_ID,
LIQUID_ACCOUNT_ID,
_constants.owedMarket,
_constants.heldMarket,
_liquidatorCache.owedWeiToLiquidate
);
}
}
function _otherAccountId() internal pure returns (uint256) {
return LIQUID_ACCOUNT_ID;
}
}pragma solidity ^0.5.0;
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*
* _Since v2.5.0:_ this module is now much more gas efficient, given net gas
* metering changes introduced in the Istanbul hardfork.
*/
contract ReentrancyGuard {
bool private _notEntered;
constructor () internal {
// Storing an initial non-zero value makes deployment a bit more
// expensive, but in exchange the refund on every call to nonReentrant
// will be lower in amount. Since refunds are capped to a percetange of
// the total transaction's gas, it is best to keep them low in cases
// like this one, to increase the likelihood of the full refund coming
// into effect.
_notEntered = true;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and make it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
// On the first call to nonReentrant, _notEntered will be true
require(_notEntered, "ReentrancyGuard: reentrant call");
// Any calls to nonReentrant after this point will fail
_notEntered = false;
_;
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_notEntered = true;
}
}pragma solidity ^0.5.5;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
* ====
*/
function isContract(address account) internal view returns (bool) {
// According to EIP-1052, 0x0 is the value returned for not-yet created accounts
// and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned
// for accounts without code, i.e. `keccak256('')`
bytes32 codehash;
bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
// solhint-disable-next-line no-inline-assembly
assembly { codehash := extcodehash(account) }
return (codehash != accountHash && codehash != 0x0);
}
/**
* @dev Converts an `address` into `address payable`. Note that this is
* simply a type cast: the actual underlying value is not changed.
*
* _Available since v2.4.0._
*/
function toPayable(address account) internal pure returns (address payable) {
return address(uint160(account));
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*
* _Available since v2.4.0._
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
// solhint-disable-next-line avoid-call-value
(bool success, ) = recipient.call.value(amount)("");
require(success, "Address: unable to send value, recipient may have reverted");
}
}pragma solidity ^0.5.0;
import "./IERC20.sol";
import "../../math/SafeMath.sol";
import "../../utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for ERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using SafeMath for uint256;
using Address for address;
function safeTransfer(IERC20 token, address to, uint256 value) internal {
callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
function safeApprove(IERC20 token, address spender, uint256 value) internal {
// safeApprove should only be called when setting an initial allowance,
// or when resetting it to zero. To increase and decrease it, use
// 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
// solhint-disable-next-line max-line-length
require((value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 newAllowance = token.allowance(address(this), spender).add(value);
callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero");
callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function callOptionalReturn(IERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves.
// A Solidity high level call has three parts:
// 1. The target address is checked to verify it contains contract code
// 2. The call itself is made, and success asserted
// 3. The return value is decoded, which in turn checks the size of the returned data.
// solhint-disable-next-line max-line-length
require(address(token).isContract(), "SafeERC20: call to non-contract");
// solhint-disable-next-line avoid-low-level-calls
(bool success, bytes memory returndata) = address(token).call(data);
require(success, "SafeERC20: low-level call failed");
if (returndata.length > 0) { // Return data is optional
// solhint-disable-next-line max-line-length
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}pragma solidity ^0.5.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP. Does not include
* the optional functions; to access them see {ERC20Detailed}.
*/
interface IERC20 {
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `recipient`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address recipient, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `sender` to `recipient` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
}pragma solidity ^0.5.0;
/**
* @dev Wrappers over Solidity's arithmetic operations with added overflow
* checks.
*
* Arithmetic operations in Solidity wrap on overflow. This can easily result
* in bugs, because programmers usually assume that an overflow raises an
* error, which is the standard behavior in high level programming languages.
* `SafeMath` restores this intuition by reverting the transaction when an
* operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*/
library SafeMath {
/**
* @dev Returns the addition of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
* - Addition cannot overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
return sub(a, b, "SafeMath: subtraction overflow");
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting with custom message on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
* - Subtraction cannot overflow.
*
* _Available since v2.4.0._
*/
function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b <= a, errorMessage);
uint256 c = a - b;
return c;
}
/**
* @dev Returns the multiplication of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `*` operator.
*
* Requirements:
* - Multiplication cannot overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
return div(a, b, "SafeMath: division by zero");
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts with custom message on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*
* _Available since v2.4.0._
*/
function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
// Solidity only automatically asserts when dividing by 0
require(b > 0, errorMessage);
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
return mod(a, b, "SafeMath: modulo by zero");
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts with custom message when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*
* _Available since v2.4.0._
*/
function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b != 0, errorMessage);
return a % b;
}
}/*
Copyright 2019 dYdX Trading Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.7;
pragma experimental ABIEncoderV2;
import { SafeMath } from "@openzeppelin/contracts/math/SafeMath.sol";
import { DolomiteMarginMath } from "./DolomiteMarginMath.sol";
/**
* @title Types
* @author dYdX
*
* Library for interacting with the basic structs used in DolomiteMargin
*/
library Types {
using DolomiteMarginMath for uint256;
// ============ Permission ============
struct OperatorArg {
address operator;
bool trusted;
}
// ============ AssetAmount ============
enum AssetDenomination {
Wei, // the amount is denominated in wei
Par // the amount is denominated in par
}
enum AssetReference {
Delta, // the amount is given as a delta from the current value
Target // the amount is given as an exact number to end up at
}
struct AssetAmount {
bool sign; // true if positive
AssetDenomination denomination;
AssetReference ref;
uint256 value;
}
// ============ Par (Principal Amount) ============
// Total borrow and supply values for a market
struct TotalPar {
uint128 borrow;
uint128 supply;
}
// Individual principal amount for an account
struct Par {
bool sign; // true if positive
uint128 value;
}
function zeroPar()
internal
pure
returns (Par memory)
{
return Par({
sign: false,
value: 0
});
}
function sub(
Par memory a,
Par memory b
)
internal
pure
returns (Par memory)
{
return add(a, negative(b));
}
function add(
Par memory a,
Par memory b
)
internal
pure
returns (Par memory)
{
Par memory result;
if (a.sign == b.sign) {
result.sign = a.sign;
result.value = SafeMath.add(a.value, b.value).to128();
} else {
if (a.value >= b.value) {
result.sign = a.sign;
result.value = SafeMath.sub(a.value, b.value).to128();
} else {
result.sign = b.sign;
result.value = SafeMath.sub(b.value, a.value).to128();
}
}
return result;
}
function equals(
Par memory a,
Par memory b
)
internal
pure
returns (bool)
{
if (a.value == b.value) {
if (a.value == 0) {
return true;
}
return a.sign == b.sign;
}
return false;
}
function negative(
Par memory a
)
internal
pure
returns (Par memory)
{
return Par({
sign: !a.sign,
value: a.value
});
}
function isNegative(
Par memory a
)
internal
pure
returns (bool)
{
return !a.sign && a.value > 0;
}
function isPositive(
Par memory a
)
internal
pure
returns (bool)
{
return a.sign && a.value > 0;
}
function isZero(
Par memory a
)
internal
pure
returns (bool)
{
return a.value == 0;
}
function isLessThanZero(
Par memory a
)
internal
pure
returns (bool)
{
return a.value > 0 && !a.sign;
}
function isGreaterThanOrEqualToZero(
Par memory a
)
internal
pure
returns (bool)
{
return isZero(a) || a.sign;
}
// ============ Wei (Token Amount) ============
// Individual token amount for an account
struct Wei {
bool sign; // true if positive
uint256 value;
}
function zeroWei()
internal
pure
returns (Wei memory)
{
return Wei({
sign: false,
value: 0
});
}
function sub(
Wei memory a,
Wei memory b
)
internal
pure
returns (Wei memory)
{
return add(a, negative(b));
}
function add(
Wei memory a,
Wei memory b
)
internal
pure
returns (Wei memory)
{
Wei memory result;
if (a.sign == b.sign) {
result.sign = a.sign;
result.value = SafeMath.add(a.value, b.value);
} else {
if (a.value >= b.value) {
result.sign = a.sign;
result.value = SafeMath.sub(a.value, b.value);
} else {
result.sign = b.sign;
result.value = SafeMath.sub(b.value, a.value);
}
}
return result;
}
function equals(
Wei memory a,
Wei memory b
)
internal
pure
returns (bool)
{
if (a.value == b.value) {
if (a.value == 0) {
return true;
}
return a.sign == b.sign;
}
return false;
}
function negative(
Wei memory a
)
internal
pure
returns (Wei memory)
{
return Wei({
sign: !a.sign,
value: a.value
});
}
function isNegative(
Wei memory a
)
internal
pure
returns (bool)
{
return !a.sign && a.value > 0;
}
function isPositive(
Wei memory a
)
internal
pure
returns (bool)
{
return a.sign && a.value > 0;
}
function isZero(
Wei memory a
)
internal
pure
returns (bool)
{
return a.value == 0;
}
}/*
Copyright 2019 dYdX Trading Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.7;
pragma experimental ABIEncoderV2;
import { IERC20Detailed } from "../interfaces/IERC20Detailed.sol";
/**
* @title Token
* @author dYdX
*
* This library contains basic functions for interacting with ERC20 tokens. Modified to work with
* tokens that don't adhere strictly to the ERC20 standard (for example tokens that don't return a
* boolean value on success).
*/
library Token {
// ============ Library Functions ============
function transfer(
address token,
address to,
uint256 amount
)
internal
{
if (amount == 0 || to == address(this)) {
return;
}
_callOptionalReturn(
token,
abi.encodeWithSelector(IERC20Detailed(token).transfer.selector, to, amount),
"Token: transfer failed"
);
}
function transferFrom(
address token,
address from,
address to,
uint256 amount
)
internal
{
if (amount == 0 || to == from) {
return;
}
// solium-disable arg-overflow
_callOptionalReturn(
token,
abi.encodeWithSelector(IERC20Detailed(token).transferFrom.selector, from, to, amount),
"Token: transferFrom failed"
);
// solium-enable arg-overflow
}
// ============ Private Functions ============
function _callOptionalReturn(address token, bytes memory data, string memory error) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves.
// A Solidity high level call has three parts:
// 1. The target address is checked to contain contract code. Not needed since tokens are manually added
// 2. The call itself is made, and success asserted
// 3. The return value is decoded, which in turn checks the size of the returned data.
// solium-disable-next-line security/no-low-level-calls
(bool success, bytes memory returnData) = token.call(data);
require(success, error);
if (returnData.length > 0) {
// Return data is optional
require(abi.decode(returnData, (bool)), error);
}
}
}/*
Copyright 2019 dYdX Trading Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.7;
pragma experimental ABIEncoderV2;
import { DolomiteMarginMath } from "./DolomiteMarginMath.sol";
/**
* @title Time
* @author dYdX
*
* Library for dealing with time, assuming timestamps fit within 32 bits (valid until year 2106)
*/
library Time {
// ============ Library Functions ============
function currentTime()
internal
view
returns (uint32)
{
return DolomiteMarginMath.to32(block.timestamp);
}
}/*
Copyright 2019 dYdX Trading Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.7;
pragma experimental ABIEncoderV2;
import { SafeMath } from "@openzeppelin/contracts/math/SafeMath.sol";
import { Account } from "./Account.sol";
import { Bits } from "./Bits.sol";
import { Cache } from "./Cache.sol";
import { Decimal } from "./Decimal.sol";
import { Interest } from "./Interest.sol";
import { EnumerableSet } from "./EnumerableSet.sol";
import { DolomiteMarginMath } from "./DolomiteMarginMath.sol";
import { Monetary } from "./Monetary.sol";
import { Require } from "./Require.sol";
import { Time } from "./Time.sol";
import { Token } from "./Token.sol";
import { Types } from "./Types.sol";
import { IERC20Detailed } from "../interfaces/IERC20Detailed.sol";
import { IInterestSetter } from "../interfaces/IInterestSetter.sol";
import { IPriceOracle } from "../interfaces/IPriceOracle.sol";
/**
* @title Storage
* @author dYdX
*
* Functions for reading, writing, and verifying state in DolomiteMargin
*/
library Storage {
using Cache for Cache.MarketCache;
using Storage for Storage.State;
using DolomiteMarginMath for uint256;
using Types for Types.Par;
using Types for Types.Wei;
using SafeMath for uint256;
using EnumerableSet for EnumerableSet.Set;
// ============ Constants ============
bytes32 private constant FILE = "Storage";
// ============ Structs ============
// All information necessary for tracking a market
struct Market {
// Contract address of the associated ERC20 token
address token;
// Whether additional borrows are allowed for this market
bool isClosing;
// Whether this market can be removed and its ID can be recycled and reused
bool isRecyclable;
// Total aggregated supply and borrow amount of the entire market
Types.TotalPar totalPar;
// Interest index of the market
Interest.Index index;
// Contract address of the price oracle for this market
IPriceOracle priceOracle;
// Contract address of the interest setter for this market
IInterestSetter interestSetter;
// Multiplier on the marginRatio for this market, IE 5% (0.05 * 1e18). This number increases the market's
// required collateralization by: reducing the user's supplied value (in terms of dollars) for this market and
// increasing its borrowed value. This is done through the following operation:
// `suppliedWei = suppliedWei + (assetValueForThisMarket / (1 + marginPremium))`
// This number increases the user's borrowed wei by multiplying it by:
// `borrowedWei = borrowedWei + (assetValueForThisMarket * (1 + marginPremium))`
Decimal.D256 marginPremium;
// Multiplier on the liquidationSpread for this market, IE 20% (0.2 * 1e18). This number increases the
// `liquidationSpread` using the following formula:
// `liquidationSpread = liquidationSpread * (1 + spreadPremium)`
// NOTE: This formula is applied up to two times - one for each market whose spreadPremium is greater than 0
// (when performing a liquidation between two markets)
Decimal.D256 spreadPremium;
// The maximum amount that can be held by the protocol. This allows the protocol to cap any additional risk
// that is inferred by allowing borrowing against low-cap or assets with increased volatility. Setting this
// value to 0 is analogous to having no limit. This value can never be below 0.
Types.Wei maxWei;
}
// The global risk parameters that govern the health and security of the system
struct RiskParams {
// Required ratio of over-collateralization
Decimal.D256 marginRatio;
// Percentage penalty incurred by liquidated accounts
Decimal.D256 liquidationSpread;
// Percentage of the borrower's interest fee that gets passed to the suppliers
Decimal.D256 earningsRate;
// The minimum absolute borrow value of an account
// There must be sufficient incentivize to liquidate undercollateralized accounts
Monetary.Value minBorrowedValue;
// The maximum number of markets a user can have a non-zero balance for a given account.
uint256 accountMaxNumberOfMarketsWithBalances;
}
// The maximum RiskParam values that can be set
struct RiskLimits {
// The highest that the ratio can be for liquidating under-water accounts
uint64 marginRatioMax;
// The highest that the liquidation rewards can be when a liquidator liquidates an account
uint64 liquidationSpreadMax;
// The highest that the supply APR can be for a market, as a proportion of the borrow rate. Meaning, a rate of
// 100% (1e18) would give suppliers all of the interest that borrowers are paying. A rate of 90% would give
// suppliers 90% of the interest that borrowers pay.
uint64 earningsRateMax;
// The highest min margin ratio premium that can be applied to a particular market. Meaning, a value of 100%
// (1e18) would require borrowers to maintain an extra 100% collateral to maintain a healthy margin ratio. This
// value works by increasing the debt owed and decreasing the supply held for the particular market by this
// amount, plus 1e18 (since a value of 10% needs to be applied as `decimal.plusOne`)
uint64 marginPremiumMax;
// The highest liquidation reward that can be applied to a particular market. This percentage is applied
// in addition to the liquidation spread in `RiskParams`. Meaning a value of 1e18 is 100%. It is calculated as:
// `liquidationSpread * Decimal.onePlus(spreadPremium)`
uint64 spreadPremiumMax;
uint128 minBorrowedValueMax;
}
// The entire storage state of DolomiteMargin
struct State {
// number of markets
uint256 numMarkets;
// marketId => Market
mapping (uint256 => Market) markets;
// token address => marketId
mapping (address => uint256) tokenToMarketId;
// Linked list from marketId to the next recycled market id
mapping(uint256 => uint256) recycledMarketIds;
// owner => account number => Account
mapping (address => mapping (uint256 => Account.Storage)) accounts;
// Addresses that can control other users accounts
mapping (address => mapping (address => bool)) operators;
// Addresses that can control all users accounts
mapping (address => bool) globalOperators;
// Addresses of auto traders that can only be called by global operators. IE for expirations
mapping (address => bool) specialAutoTraders;
// mutable risk parameters of the system
RiskParams riskParams;
// immutable risk limits of the system
RiskLimits riskLimits;
}
// ============ Functions ============
function getToken(
Storage.State storage state,
uint256 marketId
)
internal
view
returns (address)
{
return state.markets[marketId].token;
}
function getTotalPar(
Storage.State storage state,
uint256 marketId
)
internal
view
returns (Types.TotalPar memory)
{
return state.markets[marketId].totalPar;
}
function getMaxWei(
Storage.State storage state,
uint256 marketId
)
internal
view
returns (Types.Wei memory)
{
return state.markets[marketId].maxWei;
}
function getIndex(
Storage.State storage state,
uint256 marketId
)
internal
view
returns (Interest.Index memory)
{
return state.markets[marketId].index;
}
function getNumExcessTokens(
Storage.State storage state,
uint256 marketId
)
internal
view
returns (Types.Wei memory)
{
Interest.Index memory index = state.getIndex(marketId);
Types.TotalPar memory totalPar = state.getTotalPar(marketId);
address token = state.getToken(marketId);
Types.Wei memory balanceWei = Types.Wei({
sign: true,
value: IERC20Detailed(token).balanceOf(address(this))
});
(
Types.Wei memory supplyWei,
Types.Wei memory borrowWei
) = Interest.totalParToWei(totalPar, index);
// borrowWei is negative, so subtracting it makes the value more positive
return balanceWei.sub(borrowWei).sub(supplyWei);
}
function getStatus(
Storage.State storage state,
Account.Info memory account
)
internal
view
returns (Account.Status)
{
return state.accounts[account.owner][account.number].status;
}
function getPar(
Storage.State storage state,
Account.Info memory account,
uint256 marketId
)
internal
view
returns (Types.Par memory)
{
return state.accounts[account.owner][account.number].balances[marketId];
}
function getWei(
Storage.State storage state,
Account.Info memory account,
uint256 marketId,
Interest.Index memory index
)
internal
view
returns (Types.Wei memory)
{
Types.Par memory par = state.getPar(account, marketId);
if (par.isZero()) {
return Types.zeroWei();
}
return Interest.parToWei(par, index);
}
function getMarketsWithBalancesSet(
Storage.State storage state,
Account.Info memory account
)
internal
view
returns (EnumerableSet.Set storage)
{
return state.accounts[account.owner][account.number].marketsWithNonZeroBalanceSet;
}
function getMarketsWithBalances(
Storage.State storage state,
Account.Info memory account
)
internal
view
returns (uint256[] memory)
{
return state.accounts[account.owner][account.number].marketsWithNonZeroBalanceSet.values();
}
function getAccountMarketWithBalanceAtIndex(
Storage.State storage state,
Account.Info memory account,
uint256 index
)
internal
view
returns (uint256)
{
return state.accounts[account.owner][account.number].marketsWithNonZeroBalanceSet.getAtIndex(index);
}
function getNumberOfMarketsWithBalances(
Storage.State storage state,
Account.Info memory account
)
internal
view
returns (uint256)
{
return state.accounts[account.owner][account.number].marketsWithNonZeroBalanceSet.length();
}
function getAccountNumberOfMarketsWithDebt(
Storage.State storage state,
Account.Info memory account
)
internal
view
returns (uint256)
{
return state.accounts[account.owner][account.number].numberOfMarketsWithDebt;
}
function getLiquidationSpreadForPair(
Storage.State storage state,
uint256 heldMarketId,
uint256 owedMarketId
)
internal
view
returns (Decimal.D256 memory)
{
uint256 result = state.riskParams.liquidationSpread.value;
result = Decimal.mul(result, Decimal.onePlus(state.markets[heldMarketId].spreadPremium));
result = Decimal.mul(result, Decimal.onePlus(state.markets[owedMarketId].spreadPremium));
return Decimal.D256({
value: result
});
}
function fetchNewIndex(
Storage.State storage state,
uint256 marketId,
Interest.Index memory index
)
internal
view
returns (Interest.Index memory)
{
Interest.Rate memory rate = state.fetchInterestRate(marketId, index);
return Interest.calculateNewIndex(
index,
rate,
state.getTotalPar(marketId),
state.riskParams.earningsRate
);
}
function fetchInterestRate(
Storage.State storage state,
uint256 marketId,
Interest.Index memory index
)
internal
view
returns (Interest.Rate memory)
{
Types.TotalPar memory totalPar = state.getTotalPar(marketId);
(
Types.Wei memory supplyWei,
Types.Wei memory borrowWei
) = Interest.totalParToWei(totalPar, index);
Interest.Rate memory rate = state.markets[marketId].interestSetter.getInterestRate(
state.getToken(marketId),
borrowWei.value,
supplyWei.value
);
return rate;
}
function fetchPrice(
Storage.State storage state,
uint256 marketId,
address token
)
internal
view
returns (Monetary.Price memory)
{
IPriceOracle oracle = IPriceOracle(state.markets[marketId].priceOracle);
Monetary.Price memory price = oracle.getPrice(token);
Require.that(
price.value != 0,
FILE,
"Price cannot be zero",
marketId
);
return price;
}
function getAccountValues(
Storage.State storage state,
Account.Info memory account,
Cache.MarketCache memory cache,
bool adjustForLiquidity
)
internal
view
returns (Monetary.Value memory, Monetary.Value memory)
{
Monetary.Value memory supplyValue;
Monetary.Value memory borrowValue;
uint256 numMarkets = cache.getNumMarkets();
for (uint256 i = 0; i < numMarkets; i++) {
Types.Wei memory userWei = state.getWei(account, cache.getAtIndex(i).marketId, cache.getAtIndex(i).index);
if (userWei.isZero()) {
continue;
}
uint256 assetValue = userWei.value.mul(cache.getAtIndex(i).price.value);
Decimal.D256 memory adjust = Decimal.one();
if (adjustForLiquidity) {
adjust = Decimal.onePlus(state.markets[cache.getAtIndex(i).marketId].marginPremium);
}
if (userWei.sign) {
supplyValue.value = supplyValue.value.add(Decimal.div(assetValue, adjust));
} else {
borrowValue.value = borrowValue.value.add(Decimal.mul(assetValue, adjust));
}
}
return (supplyValue, borrowValue);
}
function isCollateralized(
Storage.State storage state,
Account.Info memory account,
Cache.MarketCache memory cache,
bool requireMinBorrow
)
internal
view
returns (bool)
{
if (state.getAccountNumberOfMarketsWithDebt(account) == 0) {
// The user does not have a balance with a borrow amount, so they must be collateralized
return true;
}
// get account values (adjusted for liquidity)
(
Monetary.Value memory supplyValue,
Monetary.Value memory borrowValue
) = state.getAccountValues(account, cache, /* adjustForLiquidity = */ true);
if (requireMinBorrow) {
Require.that(
borrowValue.value >= state.riskParams.minBorrowedValue.value,
FILE,
"Borrow value too low",
account.owner,
account.number
);
}
uint256 requiredMargin = Decimal.mul(borrowValue.value, state.riskParams.marginRatio);
return supplyValue.value >= borrowValue.value.add(requiredMargin);
}
function isGlobalOperator(
Storage.State storage state,
address operator
)
internal
view
returns (bool)
{
return state.globalOperators[operator];
}
function isAutoTraderSpecial(
Storage.State storage state,
address autoTrader
)
internal
view
returns (bool)
{
return state.specialAutoTraders[autoTrader];
}
function isLocalOperator(
Storage.State storage state,
address owner,
address operator
)
internal
view
returns (bool)
{
return state.operators[owner][operator];
}
function requireIsGlobalOperator(
Storage.State storage state,
address operator
)
internal
view
{
bool isValidOperator = state.isGlobalOperator(operator);
Require.that(
isValidOperator,
FILE,
"Unpermissioned global operator",
operator
);
}
function requireIsOperator(
Storage.State storage state,
Account.Info memory account,
address operator
)
internal
view
{
bool isValidOperator =
operator == account.owner
|| state.isGlobalOperator(operator)
|| state.isLocalOperator(account.owner, operator);
Require.that(
isValidOperator,
FILE,
"Unpermissioned operator",
operator
);
}
/**
* Determine and set an account's balance based on the intended balance change. Return the
* equivalent amount in wei
*/
function getNewParAndDeltaWei(
Storage.State storage state,
Account.Info memory account,
uint256 marketId,
Interest.Index memory index,
Types.AssetAmount memory amount
)
internal
view
returns (Types.Par memory, Types.Wei memory)
{
Types.Par memory oldPar = state.getPar(account, marketId);
if (amount.value == 0 && amount.ref == Types.AssetReference.Delta) {
return (oldPar, Types.zeroWei());
}
Types.Wei memory oldWei = Interest.parToWei(oldPar, index);
Types.Par memory newPar;
Types.Wei memory deltaWei;
if (amount.denomination == Types.AssetDenomination.Wei) {
deltaWei = Types.Wei({
sign: amount.sign,
value: amount.value
});
if (amount.ref == Types.AssetReference.Target) {
deltaWei = deltaWei.sub(oldWei);
}
newPar = Interest.weiToPar(oldWei.add(deltaWei), index);
} else { // AssetDenomination.Par
newPar = Types.Par({
sign: amount.sign,
value: amount.value.to128()
});
if (amount.ref == Types.AssetReference.Delta) {
newPar = oldPar.add(newPar);
}
deltaWei = Interest.parToWei(newPar, index).sub(oldWei);
}
return (newPar, deltaWei);
}
function getNewParAndDeltaWeiForLiquidation(
Storage.State storage state,
Account.Info memory account,
uint256 marketId,
Interest.Index memory index,
Types.AssetAmount memory amount
)
internal
view
returns (Types.Par memory, Types.Wei memory)
{
Types.Par memory oldPar = state.getPar(account, marketId);
Require.that(
!oldPar.isPositive(),
FILE,
"Owed balance cannot be positive",
account.owner,
account.number
);
(
Types.Par memory newPar,
Types.Wei memory deltaWei
) = state.getNewParAndDeltaWei(
account,
marketId,
index,
amount
);
// if attempting to over-repay the owed asset, bound it by the maximum
if (newPar.isPositive()) {
newPar = Types.zeroPar();
deltaWei = state.getWei(account, marketId, index).negative();
}
Require.that(
!deltaWei.isNegative() && oldPar.value >= newPar.value,
FILE,
"Owed balance cannot increase",
account.owner,
account.number
);
// if not paying back enough wei to repay any par, then bound wei to zero
if (oldPar.equals(newPar)) {
deltaWei = Types.zeroWei();
}
return (newPar, deltaWei);
}
function isVaporizable(
Storage.State storage state,
Account.Info memory account,
Cache.MarketCache memory cache
)
internal
view
returns (bool)
{
bool hasNegative = false;
uint256 numMarkets = cache.getNumMarkets();
for (uint256 i = 0; i < numMarkets; i++) {
Types.Par memory par = state.getPar(account, cache.getAtIndex(i).marketId);
if (par.isZero()) {
continue;
} else if (par.sign) {
return false;
} else {
hasNegative = true;
}
}
return hasNegative;
}
// =============== Setter Functions ===============
function updateIndex(
Storage.State storage state,
uint256 marketId
)
internal
returns (Interest.Index memory)
{
Interest.Index memory index = state.getIndex(marketId);
if (index.lastUpdate == Time.currentTime()) {
return index;
}
return state.markets[marketId].index = state.fetchNewIndex(marketId, index);
}
function setStatus(
Storage.State storage state,
Account.Info memory account,
Account.Status status
)
internal
{
state.accounts[account.owner][account.number].status = status;
}
function setPar(
Storage.State storage state,
Account.Info memory account,
uint256 marketId,
Types.Par memory newPar
)
internal
{
Types.Par memory oldPar = state.getPar(account, marketId);
if (Types.equals(oldPar, newPar)) {
// GUARD statement
return;
}
// updateTotalPar
Types.TotalPar memory totalPar = state.getTotalPar(marketId);
// roll-back oldPar
if (oldPar.sign) {
totalPar.supply = uint256(totalPar.supply).sub(oldPar.value).to128();
} else {
totalPar.borrow = uint256(totalPar.borrow).sub(oldPar.value).to128();
}
// roll-forward newPar
if (newPar.sign) {
totalPar.supply = uint256(totalPar.supply).add(newPar.value).to128();
} else {
totalPar.borrow = uint256(totalPar.borrow).add(newPar.value).to128();
}
if (oldPar.isLessThanZero() && newPar.isGreaterThanOrEqualToZero()) {
// user went from borrowing to repaying or positive
state.accounts[account.owner][account.number].numberOfMarketsWithDebt -= 1;
} else if (oldPar.isGreaterThanOrEqualToZero() && newPar.isLessThanZero()) {
// user went from zero or positive to borrowing
state.accounts[account.owner][account.number].numberOfMarketsWithDebt += 1;
}
if (newPar.isZero() && (!oldPar.isZero())) {
// User went from a non-zero balance to zero. Remove the market from the set.
state.accounts[account.owner][account.number].marketsWithNonZeroBalanceSet.remove(marketId);
} else if ((!newPar.isZero()) && oldPar.isZero()) {
// User went from zero to non-zero. Add the market to the set.
state.accounts[account.owner][account.number].marketsWithNonZeroBalanceSet.add(marketId);
}
state.markets[marketId].totalPar = totalPar;
state.accounts[account.owner][account.number].balances[marketId] = newPar;
}
/**
* Determine and set an account's balance based on a change in wei
*/
function setParFromDeltaWei(
Storage.State storage state,
Account.Info memory account,
uint256 marketId,
Interest.Index memory index,
Types.Wei memory deltaWei
)
internal
{
if (deltaWei.isZero()) {
return;
}
Types.Wei memory oldWei = state.getWei(account, marketId, index);
Types.Wei memory newWei = oldWei.add(deltaWei);
Types.Par memory newPar = Interest.weiToPar(newWei, index);
state.setPar(
account,
marketId,
newPar
);
}
/**
* Initializes the cache using the set bits
*/
function initializeCache(
Storage.State storage state,
Cache.MarketCache memory cache
) internal view {
cache.markets = new Cache.MarketInfo[](cache.marketsLength);
uint counter = 0;
// Really neat byproduct of iterating through a bitmap using the least significant bit, where each set flag
// represents the marketId, --> the initialized `cache.markets` array is sorted in O(n)!
// Meaning, this function call is O(n) where `n` is the number of markets in the cache
for (uint i = 0; i < cache.marketBitmaps.length; i++) {
uint bitmap = cache.marketBitmaps[i];
while (bitmap != 0) {
uint nextSetBit = Bits.getLeastSignificantBit(bitmap);
uint marketId = Bits.getMarketIdFromBit(i, nextSetBit);
address token = state.getToken(marketId);
Types.TotalPar memory totalPar = state.getTotalPar(marketId);
cache.markets[counter++] = Cache.MarketInfo({
marketId: marketId,
token: token,
isClosing: state.markets[marketId].isClosing,
borrowPar: totalPar.borrow,
supplyPar: totalPar.supply,
index: state.getIndex(marketId),
price: state.fetchPrice(marketId, token)
});
// unset the set bit
bitmap = Bits.unsetBit(bitmap, nextSetBit);
}
if (counter == cache.marketsLength) {
break;
}
}
assert(cache.marketsLength == counter);
}
}/*
Copyright 2019 dYdX Trading Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.7;
pragma experimental ABIEncoderV2;
/**
* @title Require
* @author dYdX
*
* Stringifies parameters to pretty-print revert messages. Costs more gas than regular require()
*/
library Require {
// ============ Constants ============
uint256 constant ASCII_ZERO = 48; // '0'
uint256 constant ASCII_RELATIVE_ZERO = 87; // 'a' - 10
uint256 constant ASCII_LOWER_EX = 120; // 'x'
bytes2 constant COLON = 0x3a20; // ': '
bytes2 constant COMMA = 0x2c20; // ', '
bytes2 constant LPAREN = 0x203c; // ' <'
byte constant RPAREN = 0x3e; // '>'
uint256 constant FOUR_BIT_MASK = 0xf;
// ============ Library Functions ============
function that(
bool must,
bytes32 file,
bytes32 reason
)
internal
pure
{
if (!must) {
revert(
string(
abi.encodePacked(
stringifyTruncated(file),
COLON,
stringifyTruncated(reason)
)
)
);
}
}
function that(
bool must,
bytes32 file,
bytes32 reason,
uint256 payloadA
)
internal
pure
{
if (!must) {
revert(
string(
abi.encodePacked(
stringifyTruncated(file),
COLON,
stringifyTruncated(reason),
LPAREN,
stringify(payloadA),
RPAREN
)
)
);
}
}
function that(
bool must,
bytes32 file,
bytes32 reason,
uint256 payloadA,
uint256 payloadB
)
internal
pure
{
if (!must) {
revert(
string(
abi.encodePacked(
stringifyTruncated(file),
COLON,
stringifyTruncated(reason),
LPAREN,
stringify(payloadA),
COMMA,
stringify(payloadB),
RPAREN
)
)
);
}
}
function that(
bool must,
bytes32 file,
bytes32 reason,
address payloadA
)
internal
pure
{
if (!must) {
revert(
string(
abi.encodePacked(
stringifyTruncated(file),
COLON,
stringifyTruncated(reason),
LPAREN,
stringify(payloadA),
RPAREN
)
)
);
}
}
function that(
bool must,
bytes32 file,
bytes32 reason,
address payloadA,
uint256 payloadB
)
internal
pure
{
if (!must) {
revert(
string(
abi.encodePacked(
stringifyTruncated(file),
COLON,
stringifyTruncated(reason),
LPAREN,
stringify(payloadA),
COMMA,
stringify(payloadB),
RPAREN
)
)
);
}
}
function that(
bool must,
bytes32 file,
bytes32 reason,
address payloadA,
uint256 payloadB,
uint256 payloadC
)
internal
pure
{
if (!must) {
revert(
string(
abi.encodePacked(
stringifyTruncated(file),
COLON,
stringifyTruncated(reason),
LPAREN,
stringify(payloadA),
COMMA,
stringify(payloadB),
COMMA,
stringify(payloadC),
RPAREN
)
)
);
}
}
function that(
bool must,
bytes32 file,
bytes32 reason,
bytes32 payloadA
)
internal
pure
{
if (!must) {
revert(
string(
abi.encodePacked(
stringifyTruncated(file),
COLON,
stringifyTruncated(reason),
LPAREN,
stringify(payloadA),
RPAREN
)
)
);
}
}
function that(
bool must,
bytes32 file,
bytes32 reason,
bytes32 payloadA,
uint256 payloadB,
uint256 payloadC
)
internal
pure
{
if (!must) {
revert(
string(
abi.encodePacked(
stringifyTruncated(file),
COLON,
stringifyTruncated(reason),
LPAREN,
stringify(payloadA),
COMMA,
stringify(payloadB),
COMMA,
stringify(payloadC),
RPAREN
)
)
);
}
}
// ============ Private Functions ============
function stringifyTruncated(
bytes32 input
)
internal
pure
returns (bytes memory)
{
// put the input bytes into the result
bytes memory result = abi.encodePacked(input);
// determine the length of the input by finding the location of the last non-zero byte
for (uint256 i = 32; i > 0; ) {
// reverse-for-loops with unsigned integer
/* solium-disable-next-line security/no-modify-for-iter-var */
i--;
// find the last non-zero byte in order to determine the length
if (result[i] != 0) {
uint256 length = i + 1;
/* solium-disable-next-line security/no-inline-assembly */
assembly {
mstore(result, length) // r.length = length;
}
return result;
}
}
// all bytes are zero
return new bytes(0);
}
function stringify(
uint256 input
)
private
pure
returns (bytes memory)
{
if (input == 0) {
return "0";
}
// get the final string length
uint256 j = input;
uint256 length;
while (j != 0) {
length++;
j /= 10;
}
// allocate the string
bytes memory bstr = new bytes(length);
// populate the string starting with the least-significant character
j = input;
for (uint256 i = length; i > 0; ) {
// reverse-for-loops with unsigned integer
/* solium-disable-next-line security/no-modify-for-iter-var */
i--;
// take last decimal digit
bstr[i] = byte(uint8(ASCII_ZERO + (j % 10)));
// remove the last decimal digit
j /= 10;
}
return bstr;
}
function stringify(
address input
)
private
pure
returns (bytes memory)
{
uint256 z = uint256(input);
// addresses are "0x" followed by 20 bytes of data which take up 2 characters each
bytes memory result = new bytes(42);
// populate the result with "0x"
result[0] = byte(uint8(ASCII_ZERO));
result[1] = byte(uint8(ASCII_LOWER_EX));
// for each byte (starting from the lowest byte), populate the result with two characters
for (uint256 i = 0; i < 20; i++) {
// each byte takes two characters
uint256 shift = i * 2;
// populate the least-significant character
result[41 - shift] = char(z & FOUR_BIT_MASK);
z = z >> 4;
// populate the most-significant character
result[40 - shift] = char(z & FOUR_BIT_MASK);
z = z >> 4;
}
return result;
}
function stringify(
bytes32 input
)
private
pure
returns (bytes memory)
{
uint256 z = uint256(input);
// bytes32 are "0x" followed by 32 bytes of data which take up 2 characters each
bytes memory result = new bytes(66);
// populate the result with "0x"
result[0] = byte(uint8(ASCII_ZERO));
result[1] = byte(uint8(ASCII_LOWER_EX));
// for each byte (starting from the lowest byte), populate the result with two characters
for (uint256 i = 0; i < 32; i++) {
// each byte takes two characters
uint256 shift = i * 2;
// populate the least-significant character
result[65 - shift] = char(z & FOUR_BIT_MASK);
z = z >> 4;
// populate the most-significant character
result[64 - shift] = char(z & FOUR_BIT_MASK);
z = z >> 4;
}
return result;
}
function char(
uint256 input
)
private
pure
returns (byte)
{
// return ASCII digit (0-9)
if (input < 10) {
return byte(uint8(input + ASCII_ZERO));
}
// return ASCII letter (a-f)
return byte(uint8(input + ASCII_RELATIVE_ZERO));
}
}/*
Copyright 2019 dYdX Trading Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.7;
pragma experimental ABIEncoderV2;
/**
* @title Monetary
* @author dYdX
*
* Library for types involving money
*/
library Monetary {
/*
* The price of a base-unit of an asset. Has `36 - token.decimals` decimals
*/
struct Price {
uint256 value;
}
/*
* Total value of an some amount of an asset. Equal to (price * amount). Has 36 decimals.
*/
struct Value {
uint256 value;
}
}/*
Copyright 2019 dYdX Trading Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.7;
pragma experimental ABIEncoderV2;
import { SafeMath } from "@openzeppelin/contracts/math/SafeMath.sol";
import { Decimal } from "./Decimal.sol";
import { DolomiteMarginMath } from "./DolomiteMarginMath.sol";
import { Time } from "./Time.sol";
import { Types } from "./Types.sol";
/**
* @title Interest
* @author dYdX
*
* Library for managing the interest rate and interest indexes of DolomiteMargin
*/
library Interest {
using DolomiteMarginMath for uint256;
using SafeMath for uint256;
// ============ Constants ============
bytes32 private constant FILE = "Interest";
uint64 constant BASE = 10**18;
// ============ Structs ============
struct Rate {
uint256 value;
}
struct Index {
uint96 borrow;
uint96 supply;
uint32 lastUpdate;
}
// ============ Library Functions ============
/**
* Get a new market Index based on the old index and market interest rate.
* Calculate interest for borrowers by using the formula rate * time. Approximates
* continuously-compounded interest when called frequently, but is much more
* gas-efficient to calculate. For suppliers, the interest rate is adjusted by the earningsRate,
* then prorated across all suppliers.
*
* @param index The old index for a market
* @param rate The current interest rate of the market
* @param totalPar The total supply and borrow par values of the market
* @param earningsRate The portion of the interest that is forwarded to the suppliers
* @return The updated index for a market
*/
function calculateNewIndex(
Index memory index,
Rate memory rate,
Types.TotalPar memory totalPar,
Decimal.D256 memory earningsRate
)
internal
view
returns (Index memory)
{
(
Types.Wei memory supplyWei,
Types.Wei memory borrowWei
) = totalParToWei(totalPar, index);
// get interest increase for borrowers
uint32 currentTime = Time.currentTime();
uint256 borrowInterest = rate.value.mul(uint256(currentTime).sub(index.lastUpdate));
// get interest increase for suppliers
uint256 supplyInterest;
if (Types.isZero(supplyWei)) {
supplyInterest = 0;
} else {
supplyInterest = Decimal.mul(borrowInterest, earningsRate);
if (borrowWei.value < supplyWei.value) {
// scale down the interest by the amount being supplied. Why? Because interest is only being paid on
// the borrowWei, which means it's split amongst all of the supplyWei. Scaling it down normalizes it
// for the suppliers to share what's being paid by borrowers
supplyInterest = DolomiteMarginMath.getPartial(supplyInterest, borrowWei.value, supplyWei.value);
}
}
assert(supplyInterest <= borrowInterest);
return Index({
borrow: DolomiteMarginMath.getPartial(index.borrow, borrowInterest, BASE).add(index.borrow).to96(),
supply: DolomiteMarginMath.getPartial(index.supply, supplyInterest, BASE).add(index.supply).to96(),
lastUpdate: currentTime
});
}
function newIndex()
internal
view
returns (Index memory)
{
return Index({
borrow: BASE,
supply: BASE,
lastUpdate: Time.currentTime()
});
}
/*
* Convert a principal amount to a token amount given an index.
*/
function parToWei(
Types.Par memory input,
Index memory index
)
internal
pure
returns (Types.Wei memory)
{
uint256 inputValue = uint256(input.value);
if (input.sign) {
return Types.Wei({
sign: true,
value: inputValue.getPartialRoundHalfUp(index.supply, BASE)
});
} else {
return Types.Wei({
sign: false,
value: inputValue.getPartialRoundHalfUp(index.borrow, BASE)
});
}
}
/*
* Convert a token amount to a principal amount given an index.
*/
function weiToPar(
Types.Wei memory input,
Index memory index
)
internal
pure
returns (Types.Par memory)
{
if (input.sign) {
return Types.Par({
sign: true,
value: input.value.getPartialRoundHalfUp(BASE, index.supply).to128()
});
} else {
return Types.Par({
sign: false,
value: input.value.getPartialRoundHalfUp(BASE, index.borrow).to128()
});
}
}
/*
* Convert the total supply and borrow principal amounts of a market to total supply and borrow
* token amounts.
*/
function totalParToWei(
Types.TotalPar memory totalPar,
Index memory index
)
internal
pure
returns (Types.Wei memory, Types.Wei memory)
{
Types.Par memory supplyPar = Types.Par({
sign: true,
value: totalPar.supply
});
Types.Par memory borrowPar = Types.Par({
sign: false,
value: totalPar.borrow
});
Types.Wei memory supplyWei = parToWei(supplyPar, index);
Types.Wei memory borrowWei = parToWei(borrowPar, index);
return (supplyWei, borrowWei);
}
}/*
Copyright 2022 Dolomite.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.7;
/**
* @author https://github.com/nomad-xyz
*/
library ExcessivelySafeCall {
function safeStaticCall(
address _target,
bytes4 _selector,
bytes memory _calldata
) internal view returns (bool isSuccess, bytes memory returnData) {
(isSuccess, returnData) = _target.staticcall(abi.encodeWithSelector(_selector, _calldata));
}
/// @notice Use when you _really_ really _really_ don't trust the called
/// contract. This prevents the called contract from causing reversion of
/// the caller in as many ways as we can.
/// @dev The main difference between this and a solidity low-level call is
/// that we limit the number of bytes that the callee can cause to be
/// copied to caller memory. This prevents stupid things like malicious
/// contracts returning 10,000,000 bytes causing a local OOG when copying
/// to memory.
/// @param _target The address to call
/// @param _gas The amount of gas to forward to the remote contract
/// @param _maxCopy The maximum number of bytes of returndata to copy
/// to memory.
/// @param _calldata The data to send to the remote contract
/// @return success and returndata, as `.call()`. Returndata is capped to
/// `_maxCopy` bytes.
function excessivelySafeCall(
address _target,
uint256 _gas,
uint16 _maxCopy,
bytes memory _calldata
) internal returns (bool, bytes memory) {
// set up for assembly call
uint256 _toCopy;
bool _success;
bytes memory _returndata = new bytes(_maxCopy);
// dispatch message to recipient
// by assembly calling "handle" function
// we call via assembly to avoid mem-copying a very large returnData
// returned by a malicious contract
/* solium-disable security/no-inline-assembly */
assembly {
_success := call(
_gas, // gas
_target, // recipient
0, // ether value
add(_calldata, 0x20), // inloc
mload(_calldata), // inlen
0, // outloc
0 // outlen
)
// limit our copy to 256 bytes
_toCopy := returndatasize()
if gt(_toCopy, _maxCopy) {
_toCopy := _maxCopy
}
// Store the length of the copied bytes
mstore(_returndata, _toCopy)
// copy the bytes from returndata[0:_toCopy]
returndatacopy(add(_returndata, 0x20), 0, _toCopy)
}
/* solium-enable security/no-inline-assembly */
return (_success, _returndata);
}
/// @notice Use when you _really_ really _really_ don't trust the called
/// contract. This prevents the called contract from causing reversion of
/// the caller in as many ways as we can.
/// @dev The main difference between this and a solidity low-level call is
/// that we limit the number of bytes that the callee can cause to be
/// copied to caller memory. This prevents stupid things like malicious
/// contracts returning 10,000,000 bytes causing a local OOG when copying
/// to memory.
/// @param _target The address to call
/// @param _gas The amount of gas to forward to the remote contract
/// @param _maxCopy The maximum number of bytes of returndata to copy
/// to memory.
/// @param _calldata The data to send to the remote contract
/// @return success and returndata, as `.call()`. Returndata is capped to
/// `_maxCopy` bytes.
function excessivelySafeStaticCall(
address _target,
uint256 _gas,
uint16 _maxCopy,
bytes memory _calldata
) internal view returns (bool, bytes memory) {
// set up for assembly call
uint256 _toCopy;
bool _success;
bytes memory _returndata = new bytes(_maxCopy);
// dispatch message to recipient
// by assembly calling "handle" function
// we call via assembly to avoid mem-copying a very large returndata
// returned by a malicious contract
/* solium-disable security/no-inline-assembly */
assembly {
_success := staticcall(
_gas, // gas
_target, // recipient
add(_calldata, 0x20), // inloc
mload(_calldata), // inlen
0, // outloc
0 // outlen
)
// limit our copy to 256 bytes
_toCopy := returndatasize()
if gt(_toCopy, _maxCopy) {
_toCopy := _maxCopy
}
// Store the length of the copied bytes
mstore(_returndata, _toCopy)
// copy the bytes from returndata[0:_toCopy]
returndatacopy(add(_returndata, 0x20), 0, _toCopy)
}
/* solium-enable security/no-inline-assembly */
return (_success, _returndata);
}
}/*
Copyright 2019 dYdX Trading Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.7;
pragma experimental ABIEncoderV2;
import { Account } from "./Account.sol";
import { Actions } from "./Actions.sol";
import { Cache } from "./Cache.sol";
import { Interest } from "./Interest.sol";
import { Monetary } from "./Monetary.sol";
import { Storage } from "./Storage.sol";
import { Types } from "./Types.sol";
/**
* @title Events
* @author dYdX
*
* Library to parse and emit logs from which the state of all accounts and indexes can be followed
*/
library Events {
using Types for Types.Wei;
using Storage for Storage.State;
// ============ Events ============
event LogIndexUpdate(
uint256 indexed market,
Interest.Index index
);
event LogOraclePrice(
uint256 indexed market,
Monetary.Price price
);
event LogOperation(
address sender
);
event LogDeposit(
address indexed accountOwner,
uint256 accountNumber,
uint256 market,
BalanceUpdate update,
address from
);
event LogWithdraw(
address indexed accountOwner,
uint256 accountNumber,
uint256 market,
BalanceUpdate update,
address to
);
event LogTransfer(
address indexed accountOneOwner,
uint256 accountOneNumber,
address indexed accountTwoOwner,
uint256 accountTwoNumber,
uint256 market,
BalanceUpdate updateOne,
BalanceUpdate updateTwo
);
event LogBuy(
address indexed accountOwner,
uint256 accountNumber,
uint256 takerMarket,
uint256 makerMarket,
BalanceUpdate takerUpdate,
BalanceUpdate makerUpdate,
address exchangeWrapper
);
event LogSell(
address indexed accountOwner,
uint256 accountNumber,
uint256 takerMarket,
uint256 makerMarket,
BalanceUpdate takerUpdate,
BalanceUpdate makerUpdate,
address exchangeWrapper
);
event LogTrade(
address indexed takerAccountOwner,
uint256 takerAccountNumber,
address indexed makerAccountOwner,
uint256 makerAccountNumber,
uint256 inputMarket,
uint256 outputMarket,
BalanceUpdate takerInputUpdate,
BalanceUpdate takerOutputUpdate,
BalanceUpdate makerInputUpdate,
BalanceUpdate makerOutputUpdate,
address autoTrader
);
event LogCall(
address indexed accountOwner,
uint256 accountNumber,
address callee
);
event LogLiquidate(
address indexed solidAccountOwner,
uint256 solidAccountNumber,
address indexed liquidAccountOwner,
uint256 liquidAccountNumber,
uint256 heldMarket,
uint256 owedMarket,
BalanceUpdate solidHeldUpdate,
BalanceUpdate solidOwedUpdate,
BalanceUpdate liquidHeldUpdate,
BalanceUpdate liquidOwedUpdate
);
event LogVaporize(
address indexed solidAccountOwner,
uint256 solidAccountNumber,
address indexed vaporAccountOwner,
uint256 vaporAccountNumber,
uint256 heldMarket,
uint256 owedMarket,
BalanceUpdate solidHeldUpdate,
BalanceUpdate solidOwedUpdate,
BalanceUpdate vaporOwedUpdate
);
// ============ Structs ============
struct BalanceUpdate {
Types.Wei deltaWei;
Types.Par newPar;
}
// ============ Internal Functions ============
function logIndexUpdate(
uint256 marketId,
Interest.Index memory index
)
internal
{
emit LogIndexUpdate(
marketId,
index
);
}
function logOraclePrice(
Cache.MarketInfo memory marketInfo
)
internal
{
emit LogOraclePrice(
marketInfo.marketId,
marketInfo.price
);
}
function logOperation()
internal
{
emit LogOperation(msg.sender);
}
function logDeposit(
Storage.State storage state,
Actions.DepositArgs memory args,
Types.Wei memory deltaWei
)
internal
{
emit LogDeposit(
args.account.owner,
args.account.number,
args.market,
getBalanceUpdate(
state,
args.account,
args.market,
deltaWei
),
args.from
);
}
function logWithdraw(
Storage.State storage state,
Actions.WithdrawArgs memory args,
Types.Wei memory deltaWei
)
internal
{
emit LogWithdraw(
args.account.owner,
args.account.number,
args.market,
getBalanceUpdate(
state,
args.account,
args.market,
deltaWei
),
args.to
);
}
function logTransfer(
Storage.State storage state,
Actions.TransferArgs memory args,
Types.Wei memory deltaWei
)
internal
{
emit LogTransfer(
args.accountOne.owner,
args.accountOne.number,
args.accountTwo.owner,
args.accountTwo.number,
args.market,
getBalanceUpdate(
state,
args.accountOne,
args.market,
deltaWei
),
getBalanceUpdate(
state,
args.accountTwo,
args.market,
deltaWei.negative()
)
);
}
function logBuy(
Storage.State storage state,
Actions.BuyArgs memory args,
Types.Wei memory takerWei,
Types.Wei memory makerWei
)
internal
{
emit LogBuy(
args.account.owner,
args.account.number,
args.takerMarket,
args.makerMarket,
getBalanceUpdate(
state,
args.account,
args.takerMarket,
takerWei
),
getBalanceUpdate(
state,
args.account,
args.makerMarket,
makerWei
),
args.exchangeWrapper
);
}
function logSell(
Storage.State storage state,
Actions.SellArgs memory args,
Types.Wei memory takerWei,
Types.Wei memory makerWei
)
internal
{
emit LogSell(
args.account.owner,
args.account.number,
args.takerMarket,
args.makerMarket,
getBalanceUpdate(
state,
args.account,
args.takerMarket,
takerWei
),
getBalanceUpdate(
state,
args.account,
args.makerMarket,
makerWei
),
args.exchangeWrapper
);
}
function logTrade(
Storage.State storage state,
Actions.TradeArgs memory args,
Types.Wei memory inputWei,
Types.Wei memory outputWei
)
internal
{
BalanceUpdate[4] memory updates = [
getBalanceUpdate(
state,
args.takerAccount,
args.inputMarket,
inputWei.negative()
),
getBalanceUpdate(
state,
args.takerAccount,
args.outputMarket,
outputWei.negative()
),
getBalanceUpdate(
state,
args.makerAccount,
args.inputMarket,
inputWei
),
getBalanceUpdate(
state,
args.makerAccount,
args.outputMarket,
outputWei
)
];
emit LogTrade(
args.takerAccount.owner,
args.takerAccount.number,
args.makerAccount.owner,
args.makerAccount.number,
args.inputMarket,
args.outputMarket,
updates[0],
updates[1],
updates[2],
updates[3],
args.autoTrader
);
}
function logCall(
Actions.CallArgs memory args
)
internal
{
emit LogCall(
args.account.owner,
args.account.number,
args.callee
);
}
function logLiquidate(
Storage.State storage state,
Actions.LiquidateArgs memory args,
Types.Wei memory heldWei,
Types.Wei memory owedWei
)
internal
{
BalanceUpdate memory solidHeldUpdate = getBalanceUpdate(
state,
args.solidAccount,
args.heldMarket,
heldWei.negative()
);
BalanceUpdate memory solidOwedUpdate = getBalanceUpdate(
state,
args.solidAccount,
args.owedMarket,
owedWei.negative()
);
BalanceUpdate memory liquidHeldUpdate = getBalanceUpdate(
state,
args.liquidAccount,
args.heldMarket,
heldWei
);
BalanceUpdate memory liquidOwedUpdate = getBalanceUpdate(
state,
args.liquidAccount,
args.owedMarket,
owedWei
);
emit LogLiquidate(
args.solidAccount.owner,
args.solidAccount.number,
args.liquidAccount.owner,
args.liquidAccount.number,
args.heldMarket,
args.owedMarket,
solidHeldUpdate,
solidOwedUpdate,
liquidHeldUpdate,
liquidOwedUpdate
);
}
function logVaporize(
Storage.State storage state,
Actions.VaporizeArgs memory args,
Types.Wei memory heldWei,
Types.Wei memory owedWei,
Types.Wei memory excessWei
)
internal
{
BalanceUpdate memory solidHeldUpdate = getBalanceUpdate(
state,
args.solidAccount,
args.heldMarket,
heldWei.negative()
);
BalanceUpdate memory solidOwedUpdate = getBalanceUpdate(
state,
args.solidAccount,
args.owedMarket,
owedWei.negative()
);
BalanceUpdate memory vaporOwedUpdate = getBalanceUpdate(
state,
args.vaporAccount,
args.owedMarket,
owedWei.add(excessWei)
);
emit LogVaporize(
args.solidAccount.owner,
args.solidAccount.number,
args.vaporAccount.owner,
args.vaporAccount.number,
args.heldMarket,
args.owedMarket,
solidHeldUpdate,
solidOwedUpdate,
vaporOwedUpdate
);
}
// ============ Private Functions ============
function getBalanceUpdate(
Storage.State storage state,
Account.Info memory account,
uint256 market,
Types.Wei memory deltaWei
)
private
view
returns (BalanceUpdate memory)
{
return BalanceUpdate({
deltaWei: deltaWei,
newPar: state.getPar(account, market)
});
}
}/*
Copyright 2021 Dolomite.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.7;
pragma experimental ABIEncoderV2;
library EnumerableSet {
struct Set {
// Storage of set values
uint256[] _values;
// Value to the index in `_values` array, plus 1 because index 0 means a value is not in the set.
mapping(uint256 => uint256) _valueToIndexMap;
}
/**
* @dev Add a value to a set. O(1).
*
* @return true if the value was added to the set, that is if it was not already present.
*/
function add(Set storage set, uint256 value) internal returns (bool) {
if (!contains(set, value)) {
set._values.push(value);
// The value is stored at length-1, but we add 1 to all indexes
// and use 0 as a sentinel value
set._valueToIndexMap[value] = set._values.length;
return true;
} else {
return false;
}
}
/**
* @dev Removes a value from a set. O(1).
*
* @return true if the value was removed from the set, that is if it was present.
*/
function remove(Set storage set, uint256 value) internal returns (bool) {
// We read and store the value's index to prevent multiple reads from the same storage slot
uint256 valueIndex = set._valueToIndexMap[value];
if (valueIndex != 0) {
// Equivalent to contains(set, value)
// To delete an element from the _values array in O(1), we swap the element to delete with the last one in
// the array, and then remove the last element (sometimes called as 'swap and pop').
// This modifies the order of the array, as noted in {at}.
uint256 toDeleteIndex = valueIndex - 1;
uint256 lastIndex = set._values.length - 1;
if (lastIndex != toDeleteIndex) {
uint256 lastValue = set._values[lastIndex];
// Move the last value to the index where the value to delete is
set._values[toDeleteIndex] = lastValue;
// Update the index for the moved value
set._valueToIndexMap[lastValue] = valueIndex; // Replace lastValue's index to valueIndex
}
// Delete the slot where the moved value was stored, which is the last index
set._values.pop();
// Delete the index for the deleted slot
delete set._valueToIndexMap[value];
return true;
} else {
return false;
}
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(Set storage set, uint256 value) internal view returns (bool) {
return set._valueToIndexMap[value] != 0;
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function length(Set storage set) internal view returns (uint256) {
return set._values.length;
}
/**
* @dev Returns the value at the corresponding index. O(1).
*/
function getAtIndex(Set storage set, uint256 index) internal view returns (uint256) {
return set._values[index];
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(Set storage set) internal view returns (uint256[] memory) {
return set._values;
}
}/*
Copyright 2019 dYdX Trading Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.7;
pragma experimental ABIEncoderV2;
import { SafeMath } from "@openzeppelin/contracts/math/SafeMath.sol";
import { Require } from "./Require.sol";
/**
* @title Math
* @author dYdX
*
* Library for non-standard Math functions
*/
library DolomiteMarginMath {
using SafeMath for uint256;
// ============ Constants ============
bytes32 private constant FILE = "Math";
// ============ Library Functions ============
/*
* Return target * (numerator / denominator).
*/
function getPartial(
uint256 target,
uint256 numerator,
uint256 denominator
)
internal
pure
returns (uint256)
{
return target.mul(numerator).div(denominator);
}
/*
* Return target * (numerator / denominator), but rounded half-up. Meaning, a result of 101.1 rounds to 102
* instead of 101.
*/
function getPartialRoundUp(
uint256 target,
uint256 numerator,
uint256 denominator
)
internal
pure
returns (uint256)
{
if (target == 0 || numerator == 0) {
// SafeMath will check for zero denominator
return SafeMath.div(0, denominator);
}
return target.mul(numerator).sub(1).div(denominator).add(1);
}
/*
* Return target * (numerator / denominator), but rounded half-up. Meaning, a result of 101.5 rounds to 102
* instead of 101.
*/
function getPartialRoundHalfUp(
uint256 target,
uint256 numerator,
uint256 denominator
)
internal
pure
returns (uint256)
{
if (target == 0 || numerator == 0) {
// SafeMath will check for zero denominator
return SafeMath.div(0, denominator);
}
uint result = target.mul(numerator);
// round the denominator comparator up to ensure a fair comparison is done on the `result`'s modulo.
// For example, 51 / 103 == 0; 51 % 103 == 51; ((103 - 1) / 2) + 1 == 52; 51 < 52, therefore no round up
return result.div(denominator).add(result.mod(denominator) >= denominator.sub(1).div(2).add(1) ? 1 : 0);
}
function to128(
uint256 number
)
internal
pure
returns (uint128)
{
uint128 result = uint128(number);
Require.that(
result == number,
FILE,
"Unsafe cast to uint128",
number
);
return result;
}
function to96(
uint256 number
)
internal
pure
returns (uint96)
{
uint96 result = uint96(number);
Require.that(
result == number,
FILE,
"Unsafe cast to uint96",
number
);
return result;
}
function to32(
uint256 number
)
internal
pure
returns (uint32)
{
uint32 result = uint32(number);
Require.that(
result == number,
FILE,
"Unsafe cast to uint32",
number
);
return result;
}
}/*
Copyright 2019 dYdX Trading Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.7;
pragma experimental ABIEncoderV2;
import { SafeMath } from "@openzeppelin/contracts/math/SafeMath.sol";
import { DolomiteMarginMath } from "./DolomiteMarginMath.sol";
/**
* @title Decimal
* @author dYdX
*
* Library that defines a fixed-point number with 18 decimal places.
*/
library Decimal {
using SafeMath for uint256;
// ============ Constants ============
uint256 constant BASE = 10**18;
// ============ Structs ============
struct D256 {
uint256 value;
}
// ============ Functions ============
function one()
internal
pure
returns (D256 memory)
{
return D256({ value: BASE });
}
function onePlus(
D256 memory d
)
internal
pure
returns (D256 memory)
{
return D256({ value: d.value.add(BASE) });
}
function mul(
uint256 target,
D256 memory d
)
internal
pure
returns (uint256)
{
return DolomiteMarginMath.getPartial(target, d.value, BASE);
}
function div(
uint256 target,
D256 memory d
)
internal
pure
returns (uint256)
{
return DolomiteMarginMath.getPartial(target, BASE, d.value);
}
}/*
Copyright 2019 dYdX Trading Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.7;
pragma experimental ABIEncoderV2;
import { Bits } from "./Bits.sol";
import { Interest } from "./Interest.sol";
import { Monetary } from "./Monetary.sol";
import { Require } from "./Require.sol";
/**
* @title Cache
* @author dYdX
*
* Library for caching information about markets
*/
library Cache {
// ============ Constants ============
bytes32 private constant FILE = "Cache";
// ============ Structs ============
struct MarketInfo {
uint marketId;
address token;
bool isClosing;
uint128 borrowPar;
uint128 supplyPar;
Interest.Index index;
Monetary.Price price;
}
struct MarketCache {
MarketInfo[] markets;
uint256[] marketBitmaps;
uint256 marketsLength;
}
// ============ Setter Functions ============
/**
* Initialize an empty cache for some given number of total markets.
*/
function create(
uint256 numMarkets
)
internal
pure
returns (MarketCache memory)
{
return MarketCache({
markets: new MarketInfo[](0),
marketBitmaps: Bits.createBitmaps(numMarkets),
marketsLength: 0
});
}
// ============ Getter Functions ============
function getNumMarkets(
MarketCache memory cache
)
internal
pure
returns (uint256)
{
return cache.markets.length;
}
function hasMarket(
MarketCache memory cache,
uint256 marketId
)
internal
pure
returns (bool)
{
return Bits.hasBit(cache.marketBitmaps, marketId);
}
function get(
MarketCache memory cache,
uint256 marketId
)
internal
pure
returns (MarketInfo memory)
{
Require.that(
cache.markets.length > 0,
FILE,
"not initialized"
);
return _getInternal(
cache.markets,
0,
cache.marketsLength,
marketId
);
}
function set(
MarketCache memory cache,
uint256 marketId
)
internal
pure
{
// Devs should not be able to call this function once the `markets` array has been initialized (non-zero length)
Require.that(
cache.markets.length == 0,
FILE,
"already initialized"
);
Bits.setBit(cache.marketBitmaps, marketId);
cache.marketsLength += 1;
}
function getAtIndex(
MarketCache memory cache,
uint256 index
)
internal
pure
returns (MarketInfo memory)
{
Require.that(
index < cache.markets.length,
FILE,
"invalid index",
index,
cache.markets.length
);
return cache.markets[index];
}
// ============ Private Functions ============
function _getInternal(
MarketInfo[] memory data,
uint beginInclusive,
uint endExclusive,
uint marketId
) private pure returns (MarketInfo memory) {
uint len = endExclusive - beginInclusive;
// If length equals 0 OR length equals 1 but the item wasn't found, revert
assert(!(len == 0 || (len == 1 && data[beginInclusive].marketId != marketId)));
uint mid = beginInclusive + len / 2;
uint midMarketId = data[mid].marketId;
if (marketId < midMarketId) {
return _getInternal(
data,
beginInclusive,
mid,
marketId
);
} else if (marketId > midMarketId) {
return _getInternal(
data,
mid + 1,
endExclusive,
marketId
);
} else {
return data[mid];
}
}
}/*
Copyright 2019 dYdX Trading Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.7;
pragma experimental ABIEncoderV2;
import { Require } from "./Require.sol";
/**
* @title Bits
* @author Dolomite
*
* Library for caching information about markets
*/
library Bits {
// ============ Constants ============
uint internal constant ONE = 1;
uint256 internal constant MAX_UINT_BITS = 256;
// ============ Functions ============
function createBitmaps(uint maxLength) internal pure returns (uint[] memory) {
return new uint[]((maxLength / MAX_UINT_BITS) + ONE);
}
function getMarketIdFromBit(
uint index,
uint bit
) internal pure returns (uint) {
return (MAX_UINT_BITS * index) + bit;
}
function setBit(
uint[] memory bitmaps,
uint marketId
) internal pure {
uint bucketIndex = marketId / MAX_UINT_BITS;
uint indexFromRight = marketId % MAX_UINT_BITS;
bitmaps[bucketIndex] |= (ONE << indexFromRight);
}
function hasBit(
uint[] memory bitmaps,
uint marketId
) internal pure returns (bool) {
uint bucketIndex = marketId / MAX_UINT_BITS;
uint indexFromRight = marketId % MAX_UINT_BITS;
uint bit = bitmaps[bucketIndex] & (ONE << indexFromRight);
return bit > 0;
}
function unsetBit(
uint bitmap,
uint bit
) internal pure returns (uint) {
return bitmap - (ONE << bit);
}
// solium-disable security/no-assign-params
function getLeastSignificantBit(uint256 x) internal pure returns (uint) {
// gas usage peaks at 350 per call
uint lsb = 255;
if (x & uint128(-1) > 0) {
lsb -= 128;
} else {
x >>= 128;
}
if (x & uint64(-1) > 0) {
lsb -= 64;
} else {
x >>= 64;
}
if (x & uint32(-1) > 0) {
lsb -= 32;
} else {
x >>= 32;
}
if (x & uint16(-1) > 0) {
lsb -= 16;
} else {
x >>= 16;
}
if (x & uint8(-1) > 0) {
lsb -= 8;
} else {
x >>= 8;
}
if (x & 0xf > 0) {
lsb -= 4;
} else {
x >>= 4;
}
if (x & 0x3 > 0) {
lsb -= 2;
} else {
x >>= 2;
// solium-enable security/no-assign-params
}
if (x & 0x1 > 0) {
lsb -= 1;
}
return lsb;
}
}/*
Copyright 2019 dYdX Trading Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.7;
pragma experimental ABIEncoderV2;
import { Account } from "./Account.sol";
import { Types } from "./Types.sol";
/**
* @title Actions
* @author dYdX
*
* Library that defines and parses valid Actions
*/
library Actions {
// ============ Constants ============
bytes32 private constant FILE = "Actions";
// ============ Enums ============
enum ActionType {
Deposit, // supply tokens
Withdraw, // borrow tokens
Transfer, // transfer balance between accounts
Buy, // buy an amount of some token (externally)
Sell, // sell an amount of some token (externally)
Trade, // trade tokens against another account
Liquidate, // liquidate an undercollateralized or expiring account
Vaporize, // use excess tokens to zero-out a completely negative account
Call // send arbitrary data to an address
}
enum AccountLayout {
OnePrimary,
TwoPrimary,
PrimaryAndSecondary
}
enum MarketLayout {
ZeroMarkets,
OneMarket,
TwoMarkets
}
// ============ Structs ============
/*
* Arguments that are passed to DolomiteMargin in an ordered list as part of a single operation.
* Each ActionArgs has an actionType which specifies which action struct that this data will be
* parsed into before being processed.
*/
struct ActionArgs {
ActionType actionType;
uint256 accountId;
Types.AssetAmount amount;
uint256 primaryMarketId;
uint256 secondaryMarketId;
address otherAddress;
uint256 otherAccountId;
bytes data;
}
// ============ Action Types ============
/*
* Moves tokens from an address to DolomiteMargin. Can either repay a borrow or provide additional supply.
*/
struct DepositArgs {
Types.AssetAmount amount;
Account.Info account;
uint256 market;
address from;
}
/*
* Moves tokens from DolomiteMargin to another address. Can either borrow tokens or reduce the amount
* previously supplied.
*/
struct WithdrawArgs {
Types.AssetAmount amount;
Account.Info account;
uint256 market;
address to;
}
/*
* Transfers balance between two accounts. The msg.sender must be an operator for both accounts.
* The amount field applies to accountOne.
* This action does not require any token movement since the trade is done internally to DolomiteMargin.
*/
struct TransferArgs {
Types.AssetAmount amount;
Account.Info accountOne;
Account.Info accountTwo;
uint256 market;
}
/*
* Acquires a certain amount of tokens by spending other tokens. Sends takerMarket tokens to the
* specified exchangeWrapper contract and expects makerMarket tokens in return. The amount field
* applies to the makerMarket.
*/
struct BuyArgs {
Types.AssetAmount amount;
Account.Info account;
uint256 makerMarket;
uint256 takerMarket;
address exchangeWrapper;
bytes orderData;
}
/*
* Spends a certain amount of tokens to acquire other tokens. Sends takerMarket tokens to the
* specified exchangeWrapper and expects makerMarket tokens in return. The amount field applies
* to the takerMarket.
*/
struct SellArgs {
Types.AssetAmount amount;
Account.Info account;
uint256 takerMarket;
uint256 makerMarket;
address exchangeWrapper;
bytes orderData;
}
/*
* Trades balances between two accounts using any external contract that implements the
* AutoTrader interface. The AutoTrader contract must be an operator for the makerAccount (for
* which it is trading on-behalf-of). The amount field applies to the makerAccount and the
* inputMarket. This proposed change to the makerAccount is passed to the AutoTrader which will
* quote a change for the makerAccount in the outputMarket (or will disallow the trade).
* This action does not require any token movement since the trade is done internally to DolomiteMargin.
*/
struct TradeArgs {
Types.AssetAmount amount;
Account.Info takerAccount;
Account.Info makerAccount;
uint256 inputMarket;
uint256 outputMarket;
address autoTrader;
bytes tradeData;
}
/*
* Each account must maintain a certain margin-ratio (specified globally). If the account falls
* below this margin-ratio, it can be liquidated by any other account. This allows anyone else
* (arbitrageurs) to repay any borrowed asset (owedMarket) of the liquidating account in
* exchange for any collateral asset (heldMarket) of the liquidAccount. The ratio is determined
* by the price ratio (given by the oracles) plus a spread (specified globally). Liquidating an
* account also sets a flag on the account that the account is being liquidated. This allows
* anyone to continue liquidating the account until there are no more borrows being taken by the
* liquidating account. Liquidators do not have to liquidate the entire account all at once but
* can liquidate as much as they choose. The liquidating flag allows liquidators to continue
* liquidating the account even if it becomes collateralized through partial liquidation or
* price movement.
*/
struct LiquidateArgs {
Types.AssetAmount amount;
Account.Info solidAccount;
Account.Info liquidAccount;
uint256 owedMarket;
uint256 heldMarket;
}
/*
* Similar to liquidate, but vaporAccounts are accounts that have only negative balances remaining. The arbitrageur
* pays back the negative asset (owedMarket) of the vaporAccount in exchange for a collateral asset (heldMarket) at
* a favorable spread. However, since the liquidAccount has no collateral assets, the collateral must come from
* DolomiteMargin's excess tokens.
*/
struct VaporizeArgs {
Types.AssetAmount amount;
Account.Info solidAccount;
Account.Info vaporAccount;
uint256 owedMarket;
uint256 heldMarket;
}
/*
* Passes arbitrary bytes of data to an external contract that implements the Callee interface.
* Does not change any asset amounts. This function may be useful for setting certain variables
* on layer-two contracts for certain accounts without having to make a separate Ethereum
* transaction for doing so. Also, the second-layer contracts can ensure that the call is coming
* from an operator of the particular account.
*/
struct CallArgs {
Account.Info account;
address callee;
bytes data;
}
// ============ Helper Functions ============
function getMarketLayout(
ActionType actionType
)
internal
pure
returns (MarketLayout)
{
if (
actionType == Actions.ActionType.Deposit
|| actionType == Actions.ActionType.Withdraw
|| actionType == Actions.ActionType.Transfer
) {
return MarketLayout.OneMarket;
}
else if (actionType == Actions.ActionType.Call) {
return MarketLayout.ZeroMarkets;
}
return MarketLayout.TwoMarkets;
}
function getAccountLayout(
ActionType actionType
)
internal
pure
returns (AccountLayout)
{
if (
actionType == Actions.ActionType.Transfer
|| actionType == Actions.ActionType.Trade
) {
return AccountLayout.TwoPrimary;
} else if (
actionType == Actions.ActionType.Liquidate
|| actionType == Actions.ActionType.Vaporize
) {
return AccountLayout.PrimaryAndSecondary;
}
return AccountLayout.OnePrimary;
}
// ============ Parsing Functions ============
function parseDepositArgs(
Account.Info[] memory accounts,
ActionArgs memory args
)
internal
pure
returns (DepositArgs memory)
{
return DepositArgs({
amount: args.amount,
account: accounts[args.accountId],
market: args.primaryMarketId,
from: args.otherAddress
});
}
function parseWithdrawArgs(
Account.Info[] memory accounts,
ActionArgs memory args
)
internal
pure
returns (WithdrawArgs memory)
{
return WithdrawArgs({
amount: args.amount,
account: accounts[args.accountId],
market: args.primaryMarketId,
to: args.otherAddress
});
}
function parseTransferArgs(
Account.Info[] memory accounts,
ActionArgs memory args
)
internal
pure
returns (TransferArgs memory)
{
return TransferArgs({
amount: args.amount,
accountOne: accounts[args.accountId],
accountTwo: accounts[args.otherAccountId],
market: args.primaryMarketId
});
}
function parseBuyArgs(
Account.Info[] memory accounts,
ActionArgs memory args
)
internal
pure
returns (BuyArgs memory)
{
return BuyArgs({
amount: args.amount,
account: accounts[args.accountId],
makerMarket: args.primaryMarketId,
takerMarket: args.secondaryMarketId,
exchangeWrapper: args.otherAddress,
orderData: args.data
});
}
function parseSellArgs(
Account.Info[] memory accounts,
ActionArgs memory args
)
internal
pure
returns (SellArgs memory)
{
return SellArgs({
amount: args.amount,
account: accounts[args.accountId],
takerMarket: args.primaryMarketId,
makerMarket: args.secondaryMarketId,
exchangeWrapper: args.otherAddress,
orderData: args.data
});
}
function parseTradeArgs(
Account.Info[] memory accounts,
ActionArgs memory args
)
internal
pure
returns (TradeArgs memory)
{
return TradeArgs({
amount: args.amount,
takerAccount: accounts[args.accountId],
makerAccount: accounts[args.otherAccountId],
inputMarket: args.primaryMarketId,
outputMarket: args.secondaryMarketId,
autoTrader: args.otherAddress,
tradeData: args.data
});
}
function parseLiquidateArgs(
Account.Info[] memory accounts,
ActionArgs memory args
)
internal
pure
returns (LiquidateArgs memory)
{
return LiquidateArgs({
amount: args.amount,
solidAccount: accounts[args.accountId],
liquidAccount: accounts[args.otherAccountId],
owedMarket: args.primaryMarketId,
heldMarket: args.secondaryMarketId
});
}
function parseVaporizeArgs(
Account.Info[] memory accounts,
ActionArgs memory args
)
internal
pure
returns (VaporizeArgs memory)
{
return VaporizeArgs({
amount: args.amount,
solidAccount: accounts[args.accountId],
vaporAccount: accounts[args.otherAccountId],
owedMarket: args.primaryMarketId,
heldMarket: args.secondaryMarketId
});
}
function parseCallArgs(
Account.Info[] memory accounts,
ActionArgs memory args
)
internal
pure
returns (CallArgs memory)
{
return CallArgs({
account: accounts[args.accountId],
callee: args.otherAddress,
data: args.data
});
}
}/*
Copyright 2019 dYdX Trading Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.7;
pragma experimental ABIEncoderV2;
import { Types } from "./Types.sol";
import { EnumerableSet } from "./EnumerableSet.sol";
/**
* @title Account
* @author dYdX
*
* Library of structs and functions that represent an account
*/
library Account {
// ============ Enums ============
/*
* Most-recently-cached account status.
*
* Normal: Can only be liquidated if the account values are violating the global margin-ratio.
* Liquid: Can be liquidated no matter the account values.
* Can be vaporized if there are no more positive account values.
* Vapor: Has only negative (or zeroed) account values. Can be vaporized.
*
*/
enum Status {
Normal,
Liquid,
Vapor
}
// ============ Structs ============
// Represents the unique key that specifies an account
struct Info {
address owner; // The address that owns the account
uint256 number; // A nonce that allows a single address to control many accounts
}
// The complete storage for any account
struct Storage {
Status status;
uint32 numberOfMarketsWithDebt;
EnumerableSet.Set marketsWithNonZeroBalanceSet;
mapping (uint256 => Types.Par) balances; // Mapping from marketId to principal
}
// ============ Library Functions ============
function equals(
Info memory a,
Info memory b
)
internal
pure
returns (bool)
{
return a.owner == b.owner && a.number == b.number;
}
}/*
Copyright 2019 dYdX Trading Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.7;
pragma experimental ABIEncoderV2;
import { Monetary } from "../lib/Monetary.sol";
/**
* @title IPriceOracle
* @author dYdX
*
* Interface that Price Oracles for DolomiteMargin must implement in order to report prices.
*/
contract IPriceOracle {
// ============ Constants ============
uint256 public constant ONE_DOLLAR = 10 ** 36;
// ============ Public Functions ============
/**
* Get the price of a token
*
* @param token The ERC20 token address of the market
* @return The USD price of a base unit of the token, then multiplied by 10^36.
* So a USD-stable coin with 18 decimal places would return 10^18.
* This is the price of the base unit rather than the price of a "human-readable"
* token amount. Every ERC20 may have a different number of decimals.
*/
function getPrice(
address token
)
public
view
returns (Monetary.Price memory);
}/*
Copyright 2019 dYdX Trading Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.7;
pragma experimental ABIEncoderV2;
import { Interest } from "../lib/Interest.sol";
/**
* @title IInterestSetter
* @author dYdX
*
* Interface that Interest Setters for DolomiteMargin must implement in order to report interest rates.
*/
interface IInterestSetter {
// ============ Public Functions ============
/**
* Get the interest rate of a token given some borrowed and supplied amounts
*
* @param token The address of the ERC20 token for the market
* @param borrowWei The total borrowed token amount for the market
* @param supplyWei The total supplied token amount for the market
* @return The interest rate per second
*/
function getInterestRate(
address token,
uint256 borrowWei,
uint256 supplyWei
)
external
view
returns (Interest.Rate memory);
}/*
Copyright 2019 dYdX Trading Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.7;
pragma experimental ABIEncoderV2;
/**
* @title IExchangeWrapper
* @author dYdX
*
* Interface that Exchange Wrappers for DolomiteMargin must implement in order to trade ERC20 tokens.
*/
interface IExchangeWrapper {
// ============ Public Functions ============
/**
* Exchange some amount of takerToken for makerToken.
*
* @param _tradeOriginator Address of the initiator of the trade (however, this value
* cannot always be trusted as it is set at the discretion of the
* msg.sender)
* @param _receiver Address to set allowance on once the trade has completed
* @param _makerToken The token to receive (target asset; IE path[path.length - 1])
* @param _takerToken The token to pay (originator asset; IE path[0])
* @param _requestedFillAmount Amount of takerToken being paid
* @param _orderData Arbitrary bytes data for any information to pass to the exchange
* @return The amount of makerToken received
*/
function exchange(
address _tradeOriginator,
address _receiver,
address _makerToken,
address _takerToken,
uint256 _requestedFillAmount,
bytes calldata _orderData
)
external
returns (uint256);
/**
* Get amount of takerToken required to buy a certain amount of makerToken for a given trade.
* Should match the takerToken amount used in exchangeForAmount. If the order cannot provide
* exactly desiredMakerToken, then it must return the price to buy the minimum amount greater
* than desiredMakerToken
*
* @param _makerToken The token to receive (target asset; IE path[path.length - 1])
* @param _takerToken The token to pay (originator asset; IE path[0])
* @param _desiredMakerToken Amount of `_makerToken` requested
* @param _orderData Arbitrary bytes data for any information to pass to the exchange
* @return Amount of `_takerToken` the needed to complete the exchange
*/
function getExchangeCost(
address _makerToken,
address _takerToken,
uint256 _desiredMakerToken,
bytes calldata _orderData
)
external
view
returns (uint256);
}/*
Copyright 2019 dYdX Trading Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.7;
pragma experimental ABIEncoderV2;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
/**
* @title IERC20
* @author dYdX
*
* Interface for using ERC20 Tokens. We have to use a special interface to call ERC20 functions so
* that we don't automatically revert when calling non-compliant tokens that have no return value for
* transfer(), transferFrom(), or approve().
*/
contract IERC20Detailed is IERC20 {
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function decimals() external view returns (uint8);
}/*
Copyright 2021 Dolomite.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity >=0.5.0;
pragma experimental ABIEncoderV2;
import { IInterestSetter } from "../interfaces/IInterestSetter.sol";
import { IPriceOracle } from "../interfaces/IPriceOracle.sol";
import { Account } from "../lib/Account.sol";
import { Actions } from "../lib/Actions.sol";
import { Decimal } from "../lib/Decimal.sol";
import { Interest } from "../lib/Interest.sol";
import { Monetary } from "../lib/Monetary.sol";
import { Storage } from "../lib/Storage.sol";
import { Types } from "../lib/Types.sol";
interface IDolomiteMargin {
// ============ Getters for Markets ============
/**
* Get the ERC20 token address for a market.
*
* @param token The token to query
* @return The token's marketId if the token is valid
*/
function getMarketIdByTokenAddress(
address token
) external view returns (uint256);
/**
* Get the ERC20 token address for a market.
*
* @param marketId The market to query
* @return The token address
*/
function getMarketTokenAddress(
uint256 marketId
) external view returns (address);
/**
* Return the maximum amount of the market that can be supplied on Dolomite. Always 0 or positive.
*
* @param marketId The market to query
* @return The max amount of the market that can be supplied
*/
function getMarketMaxWei(
uint256 marketId
)
external
view
returns (Types.Wei memory);
/**
* Return true if a particular market is in closing mode. Additional borrows cannot be taken
* from a market that is closing.
*
* @param marketId The market to query
* @return True if the market is closing
*/
function getMarketIsClosing(
uint256 marketId
)
external
view
returns (bool);
/**
* Get the price of the token for a market.
*
* @param marketId The market to query
* @return The price of each atomic unit of the token
*/
function getMarketPrice(
uint256 marketId
) external view returns (Monetary.Price memory);
/**
* Get the total number of markets.
*
* @return The number of markets
*/
function getNumMarkets() external view returns (uint256);
/**
* Get the total principal amounts (borrowed and supplied) for a market.
*
* @param marketId The market to query
* @return The total principal amounts
*/
function getMarketTotalPar(
uint256 marketId
) external view returns (Types.TotalPar memory);
/**
* Get the most recently cached interest index for a market.
*
* @param marketId The market to query
* @return The most recent index
*/
function getMarketCachedIndex(
uint256 marketId
) external view returns (Interest.Index memory);
/**
* Get the interest index for a market if it were to be updated right now.
*
* @param marketId The market to query
* @return The estimated current index
*/
function getMarketCurrentIndex(
uint256 marketId
) external view returns (Interest.Index memory);
/**
* Get the price oracle address for a market.
*
* @param marketId The market to query
* @return The price oracle address
*/
function getMarketPriceOracle(
uint256 marketId
) external view returns (IPriceOracle);
/**
* Get the interest-setter address for a market.
*
* @param marketId The market to query
* @return The interest-setter address
*/
function getMarketInterestSetter(
uint256 marketId
) external view returns (IInterestSetter);
/**
* Get the margin premium for a market. A margin premium makes it so that any positions that
* include the market require a higher collateralization to avoid being liquidated.
*
* @param marketId The market to query
* @return The market's margin premium
*/
function getMarketMarginPremium(
uint256 marketId
) external view returns (Decimal.D256 memory);
/**
* Get the spread premium for a market. A spread premium makes it so that any liquidations
* that include the market have a higher spread than the global default.
*
* @param marketId The market to query
* @return The market's spread premium
*/
function getMarketSpreadPremium(
uint256 marketId
) external view returns (Decimal.D256 memory);
/**
* Return true if this market can be removed and its ID can be recycled and reused
*
* @param marketId The market to query
* @return True if the market is recyclable
*/
function getMarketIsRecyclable(
uint256 marketId
) external view returns (bool);
/**
* Gets the recyclable markets, up to `n` length. If `n` is greater than the length of the list, 0's are returned
* for the empty slots.
*
* @param n The number of markets to get, bounded by the linked list being smaller than `n`
* @return The list of recyclable markets, in the same order held by the linked list
*/
function getRecyclableMarkets(
uint256 n
) external view returns (uint[] memory);
/**
* Get the current borrower interest rate for a market.
*
* @param marketId The market to query
* @return The current interest rate
*/
function getMarketInterestRate(
uint256 marketId
) external view returns (Interest.Rate memory);
/**
* Get basic information about a particular market.
*
* @param marketId The market to query
* @return A Storage.Market struct with the current state of the market
*/
function getMarket(
uint256 marketId
) external view returns (Storage.Market memory);
/**
* Get comprehensive information about a particular market.
*
* @param marketId The market to query
* @return A tuple containing the values:
* - A Storage.Market struct with the current state of the market
* - The current estimated interest index
* - The current token price
* - The current market interest rate
*/
function getMarketWithInfo(
uint256 marketId
)
external
view
returns (
Storage.Market memory,
Interest.Index memory,
Monetary.Price memory,
Interest.Rate memory
);
/**
* Get the number of tokens that are owed to the `owner` of DolomiteMargin. The number of excess tokens is
* calculated by taking the current number of tokens held in DolomiteMargin, adding the number of tokens owed to
* DolomiteMargin by borrowers, and subtracting the number of tokens owed to suppliers by DolomiteMargin.
*
* @param marketId The market to query
* @return The number of excess tokens
*/
function getNumExcessTokens(
uint256 marketId
) external view returns (Types.Wei memory);
// ============ Getters for Accounts ============
/**
* Get the principal value for a particular account and market.
*
* @param account The account to query
* @param marketId The market to query
* @return The principal value
*/
function getAccountPar(
Account.Info calldata account,
uint256 marketId
) external view returns (Types.Par memory);
/**
* Get the principal value for a particular account and market, with no check the market is valid. Meaning, markets
* that don't exist return 0.
*
* @param account The account to query
* @param marketId The market to query
* @return The principal value
*/
function getAccountParNoMarketCheck(
Account.Info calldata account,
uint256 marketId
) external view returns (Types.Par memory);
/**
* Get the token balance for a particular account and market.
*
* @param account The account to query
* @param marketId The market to query
* @return The token amount
*/
function getAccountWei(
Account.Info calldata account,
uint256 marketId
) external view returns (Types.Wei memory);
/**
* Get the status of an account (Normal, Liquidating, or Vaporizing).
*
* @param account The account to query
* @return The account's status
*/
function getAccountStatus(
Account.Info calldata account
) external view returns (Account.Status);
/**
* Get a list of markets that have a non-zero balance for an account
*
* @param account The account to query
* @return The non-sorted marketIds with non-zero balance for the account.
*/
function getAccountMarketsWithBalances(
Account.Info calldata account
) external view returns (uint256[] memory);
/**
* Get the number of markets that have a non-zero balance for an account
*
* @param account The account to query
* @return The non-sorted marketIds with non-zero balance for the account.
*/
function getAccountNumberOfMarketsWithBalances(
Account.Info calldata account
) external view returns (uint256);
/**
* Get the marketId for an account's market with a non-zero balance at the given index
*
* @param account The account to query
* @return The non-sorted marketIds with non-zero balance for the account.
*/
function getAccountMarketWithBalanceAtIndex(
Account.Info calldata account,
uint256 index
) external view returns (uint256);
/**
* Get the number of markets with which an account has a negative balance.
*
* @param account The account to query
* @return The non-sorted marketIds with non-zero balance for the account.
*/
function getAccountNumberOfMarketsWithDebt(
Account.Info calldata account
) external view returns (uint256);
/**
* Get the total supplied and total borrowed value of an account.
*
* @param account The account to query
* @return The following values:
* - The supplied value of the account
* - The borrowed value of the account
*/
function getAccountValues(
Account.Info calldata account
) external view returns (Monetary.Value memory, Monetary.Value memory);
/**
* Get the total supplied and total borrowed values of an account adjusted by the marginPremium
* of each market. Supplied values are divided by (1 + marginPremium) for each market and
* borrowed values are multiplied by (1 + marginPremium) for each market. Comparing these
* adjusted values gives the margin-ratio of the account which will be compared to the global
* margin-ratio when determining if the account can be liquidated.
*
* @param account The account to query
* @return The following values:
* - The supplied value of the account (adjusted for marginPremium)
* - The borrowed value of the account (adjusted for marginPremium)
*/
function getAdjustedAccountValues(
Account.Info calldata account
) external view returns (Monetary.Value memory, Monetary.Value memory);
/**
* Get an account's summary for each market.
*
* @param account The account to query
* @return The following values:
* - The market IDs for each market
* - The ERC20 token address for each market
* - The account's principal value for each market
* - The account's (supplied or borrowed) number of tokens for each market
*/
function getAccountBalances(
Account.Info calldata account
) external view returns (uint[] memory, address[] memory, Types.Par[] memory, Types.Wei[] memory);
// ============ Getters for Account Permissions ============
/**
* Return true if a particular address is approved as an operator for an owner's accounts.
* Approved operators can act on the accounts of the owner as if it were the operator's own.
*
* @param owner The owner of the accounts
* @param operator The possible operator
* @return True if operator is approved for owner's accounts
*/
function getIsLocalOperator(
address owner,
address operator
) external view returns (bool);
/**
* Return true if a particular address is approved as a global operator. Such an address can
* act on any account as if it were the operator's own.
*
* @param operator The address to query
* @return True if operator is a global operator
*/
function getIsGlobalOperator(
address operator
) external view returns (bool);
/**
* Checks if the autoTrader can only be called invoked by a global operator
*
* @param autoTrader The trader that should be checked for special call privileges.
*/
function getIsAutoTraderSpecial(address autoTrader) external view returns (bool);
// ============ Getters for Risk Params ============
/**
* Get the global minimum margin-ratio that every position must maintain to prevent being
* liquidated.
*
* @return The global margin-ratio
*/
function getMarginRatio() external view returns (Decimal.D256 memory);
/**
* Get the global liquidation spread. This is the spread between oracle prices that is used to incentivize the
* liquidation of risky positions.
*
* @return The global liquidation spread
*/
function getLiquidationSpread() external view returns (Decimal.D256 memory);
/**
* Get the adjusted liquidation spread for some market pair. This is equal to the global
* liquidation spread multiplied by (1 + spreadPremium) for each of the two markets.
*
* @param heldMarketId The market for which the account has collateral
* @param owedMarketId The market for which the account has borrowed tokens
* @return The adjusted liquidation spread
*/
function getLiquidationSpreadForPair(
uint256 heldMarketId,
uint256 owedMarketId
) external view returns (Decimal.D256 memory);
/**
* Get the global earnings-rate variable that determines what percentage of the interest paid
* by borrowers gets passed-on to suppliers.
*
* @return The global earnings rate
*/
function getEarningsRate() external view returns (Decimal.D256 memory);
/**
* Get the global minimum-borrow value which is the minimum value of any new borrow on DolomiteMargin.
*
* @return The global minimum borrow value
*/
function getMinBorrowedValue() external view returns (Monetary.Value memory);
/**
* Get all risk parameters in a single struct.
*
* @return All global risk parameters
*/
function getRiskParams() external view returns (Storage.RiskParams memory);
/**
* Get all risk parameter limits in a single struct. These are the maximum limits at which the
* risk parameters can be set by the admin of DolomiteMargin.
*
* @return All global risk parameter limits
*/
function getRiskLimits() external view returns (Storage.RiskLimits memory);
// ============ Write Functions ============
/**
* The main entry-point to DolomiteMargin that allows users and contracts to manage accounts.
* Take one or more actions on one or more accounts. The msg.sender must be the owner or
* operator of all accounts except for those being liquidated, vaporized, or traded with.
* One call to operate() is considered a singular "operation". Account collateralization is
* ensured only after the completion of the entire operation.
*
* @param accounts A list of all accounts that will be used in this operation. Cannot contain
* duplicates. In each action, the relevant account will be referred-to by its
* index in the list.
* @param actions An ordered list of all actions that will be taken in this operation. The
* actions will be processed in order.
*/
function operate(
Account.Info[] calldata accounts,
Actions.ActionArgs[] calldata actions
) external;
/**
* Approves/disapproves any number of operators. An operator is an external address that has the
* same permissions to manipulate an account as the owner of the account. Operators are simply
* addresses and therefore may either be externally-owned Ethereum accounts OR smart contracts.
*
* Operators are also able to act as AutoTrader contracts on behalf of the account owner if the
* operator is a smart contract and implements the IAutoTrader interface.
*
* @param args A list of OperatorArgs which have an address and a boolean. The boolean value
* denotes whether to approve (true) or revoke approval (false) for that address.
*/
function setOperators(
Types.OperatorArg[] calldata args
) external;
// =========================================
// ============ Owner Functions ============
// =========================================
// ============ Token Functions ============
/**
* Withdraw an ERC20 token for which there is an associated market. Only excess tokens can be withdrawn. The number
* of excess tokens is calculated by taking the current number of tokens held in DolomiteMargin, adding the number
* of tokens owed to DolomiteMargin by borrowers, and subtracting the number of tokens owed to suppliers by
* DolomiteMargin.
*/
function ownerWithdrawExcessTokens(
uint256 marketId,
address recipient
)
external
returns (uint256);
/**
* Withdraw an ERC20 token for which there is no associated market.
*/
function ownerWithdrawUnsupportedTokens(
address token,
address recipient
)
external
returns (uint256);
// ============ Market Functions ============
/**
* Sets the number of non-zero balances an account may have within the same `accountIndex`. This ensures a user
* cannot DOS the system by filling their account with non-zero balances (which linearly increases gas costs when
* checking collateralization) and disallowing themselves to close the position, because the number of gas units
* needed to process their transaction exceed the block's gas limit. In turn, this would prevent the user from also
* being liquidated, causing the all of the capital to be "stuck" in the position.
*
* Lowering this number does not "freeze" user accounts that have more than the new limit of balances, because this
* variable is enforced by checking the users number of non-zero balances against the max or if it sizes down before
* each transaction finishes.
*/
function ownerSetAccountMaxNumberOfMarketsWithBalances(
uint256 accountMaxNumberOfMarketsWithBalances
)
external;
/**
* Add a new market to DolomiteMargin. Must be for a previously-unsupported ERC20 token.
*/
function ownerAddMarket(
address token,
IPriceOracle priceOracle,
IInterestSetter interestSetter,
Decimal.D256 calldata marginPremium,
Decimal.D256 calldata spreadPremium,
uint256 maxWei,
bool isClosing,
bool isRecyclable
)
external;
/**
* Removes a market from DolomiteMargin, sends any remaining tokens in this contract to `salvager` and invokes the
* recyclable callback
*/
function ownerRemoveMarkets(
uint[] calldata marketIds,
address salvager
)
external;
/**
* Set (or unset) the status of a market to "closing". The borrowedValue of a market cannot increase while its
* status is "closing".
*/
function ownerSetIsClosing(
uint256 marketId,
bool isClosing
)
external;
/**
* Set the price oracle for a market.
*/
function ownerSetPriceOracle(
uint256 marketId,
IPriceOracle priceOracle
)
external;
/**
* Set the interest-setter for a market.
*/
function ownerSetInterestSetter(
uint256 marketId,
IInterestSetter interestSetter
)
external;
/**
* Set a premium on the minimum margin-ratio for a market. This makes it so that any positions that include this
* market require a higher collateralization to avoid being liquidated.
*/
function ownerSetMarginPremium(
uint256 marketId,
Decimal.D256 calldata marginPremium
)
external;
/**
* Sets the maximum supply wei for a given `marketId`.
*/
function ownerSetMaxWei(
uint256 marketId,
uint256 maxWei
)
external;
/**
* Set a premium on the liquidation spread for a market. This makes it so that any liquidations that include this
* market have a higher spread than the global default.
*/
function ownerSetSpreadPremium(
uint256 marketId,
Decimal.D256 calldata spreadPremium
)
external;
// ============ Risk Functions ============
/**
* Set the global minimum margin-ratio that every position must maintain to prevent being liquidated.
*/
function ownerSetMarginRatio(
Decimal.D256 calldata ratio
)
external;
/**
* Set the global liquidation spread. This is the spread between oracle prices that incentivizes the liquidation of
* risky positions.
*/
function ownerSetLiquidationSpread(
Decimal.D256 calldata spread
)
external;
/**
* Set the global earnings-rate variable that determines what percentage of the interest paid by borrowers gets
* passed-on to suppliers.
*/
function ownerSetEarningsRate(
Decimal.D256 calldata earningsRate
)
external;
/**
* Set the global minimum-borrow value which is the minimum value of any new borrow on DolomiteMargin.
*/
function ownerSetMinBorrowedValue(
Monetary.Value calldata minBorrowedValue
)
external;
// ============ Global Operator Functions ============
/**
* Approve (or disapprove) an address that is permissioned to be an operator for all accounts in DolomiteMargin.
* Intended only to approve smart-contracts.
*/
function ownerSetGlobalOperator(
address operator,
bool approved
)
external;
/**
* Approve (or disapprove) an auto trader that can only be called by a global operator. IE for expirations
*/
function ownerSetAutoTraderSpecial(
address autoTrader,
bool special
)
external;
// ============ Owner Functions ============
/**
* @dev Returns the address of the current owner.
*/
function owner() external view returns (address);
/**
* @dev Returns true if the caller is the current owner.
*/
function isOwner() external view returns (bool);
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions anymore. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby removing any functionality that is only available to the owner.
*/
function renounceOwnership() external;
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) external;
}/*
Copyright 2022 Dolomite.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.7;
pragma experimental ABIEncoderV2;
import { SafeMath } from "@openzeppelin/contracts/math/SafeMath.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { ReentrancyGuard } from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import { IDolomiteMargin } from "../../protocol/interfaces/IDolomiteMargin.sol";
import { IExchangeWrapper } from "../../protocol/interfaces/IExchangeWrapper.sol";
import { Account } from "../../protocol/lib/Account.sol";
import { Actions } from "../../protocol/lib/Actions.sol";
import { Decimal } from "../../protocol/lib/Decimal.sol";
import { Interest } from "../../protocol/lib/Interest.sol";
import { DolomiteMarginMath } from "../../protocol/lib/DolomiteMarginMath.sol";
import { Monetary } from "../../protocol/lib/Monetary.sol";
import { Require } from "../../protocol/lib/Require.sol";
import { Time } from "../../protocol/lib/Time.sol";
import { Types } from "../../protocol/lib/Types.sol";
import { OnlyDolomiteMargin } from "../helpers/OnlyDolomiteMargin.sol";
import { ERC20Lib } from "../lib/ERC20Lib.sol";
/**
* @title ParaswapTrader
* @author Dolomite
*
* Contract for performing an external trade with Paraswap.
*/
contract ParaswapTrader is OnlyDolomiteMargin, IExchangeWrapper {
// ============ Constants ============
bytes32 private constant FILE = "ParaswapTrader";
// ============ Storage ============
address public PARASWAP_AUGUSTUS_ROUTER;
address public PARASWAP_TRANSFER_PROXY;
// ============ Constructor ============
constructor(
address _paraswapAugustusRouter,
address _paraswapTransferProxy,
address _dolomiteMargin
)
public
OnlyDolomiteMargin(_dolomiteMargin)
{
PARASWAP_AUGUSTUS_ROUTER = _paraswapAugustusRouter;
PARASWAP_TRANSFER_PROXY = _paraswapTransferProxy;
}
// ============ Public Functions ============
function exchange(
address /* _tradeOriginator */,
address _receiver,
address _makerToken,
address _takerToken,
uint256 _requestedFillAmount,
bytes calldata _orderData
)
external
onlyDolomiteMargin(msg.sender)
returns (uint256) {
ERC20Lib.checkAllowanceAndApprove(_takerToken, PARASWAP_TRANSFER_PROXY, _requestedFillAmount);
(uint256 minAmountOutWei, bytes memory paraswapCallData) = abi.decode(_orderData, (uint256, bytes));
_callAndCheckSuccess(paraswapCallData);
uint256 amount = IERC20(_makerToken).balanceOf(address(this));
Require.that(
amount >= minAmountOutWei,
FILE,
"insufficient output amount",
amount,
minAmountOutWei
);
ERC20Lib.checkAllowanceAndApprove(_makerToken, _receiver, amount);
return amount;
}
function getExchangeCost(
address,
address,
uint256,
bytes calldata
)
external
view
returns (uint256) {
revert(string(abi.encodePacked(Require.stringifyTruncated(FILE), "::getExchangeCost: not implemented")));
}
// ============ Private Functions ============
function _callAndCheckSuccess(bytes memory _paraswapCallData) internal {
// solium-disable-next-line security/no-low-level-calls
(bool success, bytes memory result) = PARASWAP_AUGUSTUS_ROUTER.call(_paraswapCallData);
if (!success) {
if (result.length < 68) {
revert(string(abi.encodePacked(Require.stringifyTruncated(FILE), ": revert")));
} else {
// solium-disable-next-line security/no-inline-assembly
assembly {
result := add(result, 0x04)
}
revert(string(abi.encodePacked(Require.stringifyTruncated(FILE), ": ", abi.decode(result, (string)))));
}
}
}
}/*
Copyright 2022 Dolomite.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.7;
pragma experimental ABIEncoderV2;
import { ReentrancyGuard } from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import { IDolomiteMargin } from "../../protocol/interfaces/IDolomiteMargin.sol";
import { Account } from "../../protocol/lib/Account.sol";
import { Actions } from "../../protocol/lib/Actions.sol";
import { Require } from "../../protocol/lib/Require.sol";
import { Types } from "../../protocol/lib/Types.sol";
import { HasLiquidatorRegistry } from "../helpers/HasLiquidatorRegistry.sol";
import { LiquidatorProxyBase } from "../helpers/LiquidatorProxyBase.sol";
import { IExpiry } from "../interfaces/IExpiry.sol";
import { AccountActionLib } from "../lib/AccountActionLib.sol";
import { ParaswapTrader } from "../traders/ParaswapTrader.sol";
/**
* @title LiquidatorProxyV2WithExternalLiquidity
* @author Dolomite
*
* Contract for liquidating other accounts in DolomiteMargin and atomically selling off collateral via Paraswap
* liquidity aggregation
*/
contract LiquidatorProxyV2WithExternalLiquidity is ReentrancyGuard, ParaswapTrader, LiquidatorProxyBase {
// ============ Constants ============
bytes32 private constant FILE = "LiquidatorProxyV2";
// ============ Storage ============
IExpiry public EXPIRY_PROXY;
// ============ Constructor ============
constructor (
address _expiryProxy,
address _paraswapAugustusRouter,
address _paraswapTransferProxy,
address _dolomiteMargin,
address _liquidatorAssetRegistry
)
public
ParaswapTrader(
_paraswapAugustusRouter,
_paraswapTransferProxy,
_dolomiteMargin
)
HasLiquidatorRegistry(
_liquidatorAssetRegistry
)
{
EXPIRY_PROXY = IExpiry(_expiryProxy);
}
// ============ Public Functions ============
/**
* Liquidate liquidAccount using solidAccount. This contract and the msg.sender to this contract must both be
* operators for the solidAccount.
*
* @param _solidAccount The account that will do the liquidating
* @param _liquidAccount The account that will be liquidated
* @param _owedMarket The owed market whose borrowed value will be added to `owedWeiToLiquidate`
* @param _heldMarket The held market whose collateral will be recovered to take on the debt of
* `owedMarket`
* @param _expiry The time at which the position expires, if this liquidation is for closing
* an expired position. Else, 0.
* @param _paraswapCallData The calldata to be passed along to Paraswap's router for liquidation
*/
function liquidate(
Account.Info memory _solidAccount,
Account.Info memory _liquidAccount,
uint256 _owedMarket,
uint256 _heldMarket,
uint256 _expiry,
bytes memory _paraswapCallData
)
public
nonReentrant
requireIsAssetWhitelistedForLiquidation(_heldMarket)
requireIsAssetWhitelistedForLiquidation(_owedMarket)
{
// put all values that will not change into a single struct
LiquidatorProxyConstants memory constants;
constants.dolomiteMargin = DOLOMITE_MARGIN;
constants.solidAccount = _solidAccount;
constants.liquidAccount = _liquidAccount;
constants.heldMarket = _heldMarket;
constants.owedMarket = _owedMarket;
_checkConstants(constants, _expiry);
constants.liquidMarkets = constants.dolomiteMargin.getAccountMarketsWithBalances(_liquidAccount);
constants.markets = _getMarketInfos(
constants.dolomiteMargin,
constants.dolomiteMargin.getAccountMarketsWithBalances(_solidAccount),
constants.liquidMarkets
);
constants.expiryProxy = _expiry > 0 ? EXPIRY_PROXY: IExpiry(address(0)); // don't read EXPIRY; it's not needed
constants.expiry = uint32(_expiry);
LiquidatorProxyCache memory cache = _initializeCache(constants);
// validate the msg.sender and that the liquidAccount can be liquidated
_checkBasicRequirements(constants);
// get the max liquidation amount
_calculateAndSetMaxLiquidationAmount(cache);
Account.Info[] memory accounts = _constructAccountsArray(constants);
// execute the liquidations
constants.dolomiteMargin.operate(
accounts,
_constructActionsArray(
constants,
cache,
/* _solidAccountId = */ 0, // solium-disable-line indentation
/* _liquidAccount = */ 1, // solium-disable-line indentation
_paraswapCallData
)
);
}
// ============ Internal Functions ============
function _constructAccountsArray(
LiquidatorProxyConstants memory _constants
)
internal
pure
returns (Account.Info[] memory)
{
Account.Info[] memory accounts = new Account.Info[](2);
accounts[0] = _constants.solidAccount;
accounts[1] = _constants.liquidAccount;
return accounts;
}
function _constructActionsArray(
LiquidatorProxyConstants memory _constants,
LiquidatorProxyCache memory _cache,
uint256 _solidAccountId,
uint256 _liquidAccountId,
bytes memory _paraswapCallData
)
internal
view
returns (Actions.ActionArgs[] memory)
{
Actions.ActionArgs[] memory actions = new Actions.ActionArgs[](2);
if (_constants.expiry > 0) {
// First action is a trade for closing the expired account
// accountId is solidAccount; otherAccountId is liquidAccount
actions[0] = AccountActionLib.encodeExpiryLiquidateAction(
_solidAccountId,
_liquidAccountId,
_constants.owedMarket,
_constants.heldMarket,
address(_constants.expiryProxy),
_constants.expiry,
_cache.solidHeldUpdateWithReward,
_cache.owedWeiToLiquidate,
_cache.flipMarketsForExpiration
);
} else {
// First action is a liquidation
// accountId is solidAccount; otherAccountId is liquidAccount
actions[0] = AccountActionLib.encodeLiquidateAction(
_solidAccountId,
_liquidAccountId,
_constants.owedMarket,
_constants.heldMarket,
_cache.owedWeiToLiquidate
);
}
actions[1] = AccountActionLib.encodeExternalSellAction(
_solidAccountId,
_constants.heldMarket,
_constants.owedMarket,
/* _trader = */ address(this), // solium-disable-line indentation
_cache.solidHeldUpdateWithReward,
_cache.owedWeiToLiquidate,
_paraswapCallData
);
return actions;
}
}/*
Copyright 2022 Dolomite.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.7;
pragma experimental ABIEncoderV2;
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
/**
* @title ERC20Helper
* @author Dolomite
*
* Library contract for reusable token actions
*/
library ERC20Lib {
using SafeERC20 for IERC20;
// ============ Functions ============
function checkAllowanceAndApprove(
address token,
address spender,
uint256 amount
) internal {
if (IERC20(token).allowance(address(this), spender) < amount) {
IERC20(token).safeApprove(spender, uint(- 1));
}
}
}/*
Copyright 2022 Dolomite.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.7;
pragma experimental ABIEncoderV2;
import { IDolomiteMargin } from "../../protocol/interfaces/IDolomiteMargin.sol";
import { Account } from "../../protocol/lib/Account.sol";
import { Require } from "../../protocol/lib/Require.sol";
import { Types } from "../../protocol/lib/Types.sol";
/**
* @title AccountBalanceLib
* @author Dolomite
*
* Library contract that checks a user's balance after an operation to be non-negative
*/
library AccountBalanceLib {
using Types for Types.Par;
// ============ Constants ============
bytes32 private constant FILE = "AccountBalanceLib";
// ============ Types ============
/// Checks that either BOTH, FROM, or TO accounts do not have negative balances
enum BalanceCheckFlag {
Both,
From,
To,
None
}
// ============ Functions ============
/**
* Checks that the account's balance is non-negative. Reverts if the check fails
*/
function verifyBalanceIsNonNegative(
IDolomiteMargin dolomiteMargin,
address _owner,
uint256 _accountIndex,
uint256 _marketId
) internal view {
Account.Info memory account = Account.Info(_owner, _accountIndex);
Types.Par memory par = dolomiteMargin.getAccountPar(account, _marketId);
Require.that(
par.isPositive() || par.isZero(),
FILE,
"account cannot go negative",
_owner,
_accountIndex,
_marketId
);
}
}/*
Copyright 2022 Dolomite.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.7;
pragma experimental ABIEncoderV2;
import { IDolomiteMargin } from "../../protocol/interfaces/IDolomiteMargin.sol";
import { Account } from "../../protocol/lib/Account.sol";
import { Actions } from "../../protocol/lib/Actions.sol";
import { Require } from "../../protocol/lib/Require.sol";
import { Types } from "../../protocol/lib/Types.sol";
import { IExpiry } from "../interfaces/IExpiry.sol";
import { AccountBalanceLib } from "./AccountBalanceLib.sol";
/**
* @title AccountActionLib
* @author Dolomite
*
* Library contract that makes specific actions easy to call
*/
library AccountActionLib {
// ============ Constants ============
bytes32 private constant FILE = "AccountActionLib";
uint256 constant ALL = uint256(-1);
// ============ Functions ============
function all() internal pure returns (uint256) {
return ALL;
}
// ========================= Operation Functions =========================
function deposit(
IDolomiteMargin _dolomiteMargin,
address _accountOwner,
address _fromAccount,
uint256 _toAccountNumber,
uint256 _marketId,
Types.AssetAmount memory _amount
) internal {
Account.Info[] memory accounts = new Account.Info[](1);
accounts[0] = Account.Info({
owner: _accountOwner,
number: _toAccountNumber
});
Actions.ActionArgs[] memory actions = new Actions.ActionArgs[](1);
actions[0] = encodeDepositAction(
/* _accountId = */ 0, // solium-disable-line indentation
_marketId,
_amount,
_fromAccount
);
_dolomiteMargin.operate(accounts, actions);
}
/**
* Withdraws `_marketId` from `_fromAccount` to `_toAccount`
*/
function withdraw(
IDolomiteMargin _dolomiteMargin,
address _accountOwner,
uint256 _fromAccountNumber,
address _toAccount,
uint256 _marketId,
Types.AssetAmount memory _amount,
AccountBalanceLib.BalanceCheckFlag _balanceCheckFlag
) internal {
Account.Info[] memory accounts = new Account.Info[](1);
accounts[0] = Account.Info({
owner: _accountOwner,
number: _fromAccountNumber
});
Actions.ActionArgs[] memory actions = new Actions.ActionArgs[](1);
actions[0] = encodeWithdrawalAction(
/* _accountId = */ 0, // solium-disable-line indentation
_marketId,
_amount,
_toAccount
);
_dolomiteMargin.operate(accounts, actions);
if (
_balanceCheckFlag == AccountBalanceLib.BalanceCheckFlag.Both
|| _balanceCheckFlag == AccountBalanceLib.BalanceCheckFlag.From
) {
AccountBalanceLib.verifyBalanceIsNonNegative(
_dolomiteMargin,
accounts[0].owner,
_fromAccountNumber,
_marketId
);
}
}
/**
* Transfers `_marketId` from `_fromAccount` to `_toAccount`
*/
function transfer(
IDolomiteMargin _dolomiteMargin,
address _fromAccountOwner,
uint256 _fromAccountNumber,
address _toAccountOwner,
uint256 _toAccountNumber,
uint256 _marketId,
Types.AssetAmount memory _amount,
AccountBalanceLib.BalanceCheckFlag _balanceCheckFlag
) internal {
Account.Info[] memory accounts = new Account.Info[](2);
accounts[0] = Account.Info({
owner: _fromAccountOwner,
number: _fromAccountNumber
});
accounts[1] = Account.Info({
owner: _toAccountOwner,
number: _toAccountNumber
});
Actions.ActionArgs[] memory actions = new Actions.ActionArgs[](1);
actions[0] = Actions.ActionArgs({
actionType: Actions.ActionType.Transfer,
accountId: 0,
amount: _amount,
primaryMarketId: _marketId,
secondaryMarketId: 0,
otherAddress: address(0),
otherAccountId: 1,
data: bytes("")
});
_dolomiteMargin.operate(accounts, actions);
if (
_balanceCheckFlag == AccountBalanceLib.BalanceCheckFlag.Both
|| _balanceCheckFlag == AccountBalanceLib.BalanceCheckFlag.From
) {
AccountBalanceLib.verifyBalanceIsNonNegative(
_dolomiteMargin,
_fromAccountOwner,
_fromAccountNumber,
_marketId
);
}
if (
_balanceCheckFlag == AccountBalanceLib.BalanceCheckFlag.Both
|| _balanceCheckFlag == AccountBalanceLib.BalanceCheckFlag.To
) {
AccountBalanceLib.verifyBalanceIsNonNegative(
_dolomiteMargin,
_toAccountOwner,
_toAccountNumber,
_marketId
);
}
}
// ========================= Encoding Functions =========================
function encodeCallAction(
uint256 _accountId,
address _callee,
bytes memory _callData
) internal pure returns (Actions.ActionArgs memory) {
return Actions.ActionArgs({
actionType : Actions.ActionType.Call,
accountId : _accountId,
amount : Types.AssetAmount({
sign: true,
denomination: Types.AssetDenomination.Wei,
ref: Types.AssetReference.Delta,
value: 0
}),
primaryMarketId : 0,
secondaryMarketId : 0,
otherAddress : _callee,
otherAccountId : 0,
data : _callData
});
}
function encodeDepositAction(
uint256 _accountId,
uint256 _marketId,
Types.AssetAmount memory _amount,
address _fromAccount
) internal pure returns (Actions.ActionArgs memory) {
return Actions.ActionArgs({
actionType: Actions.ActionType.Deposit,
accountId: _accountId,
amount: _amount,
primaryMarketId: _marketId,
secondaryMarketId: 0,
otherAddress: _fromAccount,
otherAccountId: 0,
data: bytes("")
});
}
function encodeExpirationAction(
Account.Info memory _account,
uint256 _accountId,
uint256 _owedMarketId,
address _expiry,
uint256 _expiryTimeDelta
) internal pure returns (Actions.ActionArgs memory) {
Require.that(
_expiryTimeDelta == uint32(_expiryTimeDelta),
FILE,
"invalid expiry time"
);
IExpiry.SetExpiryArg[] memory expiryArgs = new IExpiry.SetExpiryArg[](1);
expiryArgs[0] = IExpiry.SetExpiryArg({
account : _account,
marketId : _owedMarketId,
timeDelta : uint32(_expiryTimeDelta),
forceUpdate : true
});
return encodeCallAction(
_accountId,
_expiry,
abi.encode(IExpiry.CallFunctionType.SetExpiry, expiryArgs)
);
}
function encodeExpiryLiquidateAction(
uint256 _solidAccountId,
uint256 _liquidAccountId,
uint256 _owedMarketId,
uint256 _heldMarketId,
address _expiryProxy,
uint32 _expiry,
uint256 _solidHeldUpdateWithReward,
uint256 _owedWeiToLiquidate,
bool _flipMarkets
) internal pure returns (Actions.ActionArgs memory) {
Types.AssetAmount memory assetAmount;
if (!_flipMarkets) {
// Make the amount positive so the liquid account's owedMarket goes up (gets repaid).
assetAmount = Types.AssetAmount({
sign: true,
denomination: Types.AssetDenomination.Wei,
ref: Types.AssetReference.Delta,
value: _owedWeiToLiquidate
});
} else {
assert(_flipMarkets);
// Make the amount negative so the liquid account's heldMarket goes down (gets spent to repay owedMarket).
assetAmount = Types.AssetAmount({
sign: false,
denomination: Types.AssetDenomination.Wei,
ref: Types.AssetReference.Delta,
value: _solidHeldUpdateWithReward
});
}
return Actions.ActionArgs({
actionType: Actions.ActionType.Trade,
accountId: _solidAccountId,
amount: assetAmount,
primaryMarketId: !_flipMarkets ? _owedMarketId : _heldMarketId,
secondaryMarketId: !_flipMarkets ? _heldMarketId : _owedMarketId,
otherAddress: _expiryProxy,
otherAccountId: _liquidAccountId,
data: abi.encode(_owedMarketId, _expiry)
});
}
function encodeInternalTradeAction(
uint256 _fromAccountId,
uint256 _toAccountId,
uint256 _primaryMarketId,
uint256 _secondaryMarketId,
address _traderAddress,
uint256 _amountInWei,
uint256 _amountOutMinWei
) internal pure returns (Actions.ActionArgs memory) {
return Actions.ActionArgs({
actionType : Actions.ActionType.Trade,
accountId : _fromAccountId,
// solium-disable-next-line arg-overflow
amount : Types.AssetAmount({
sign: true,
denomination: Types.AssetDenomination.Wei,
ref: Types.AssetReference.Delta,
value: _amountInWei
}),
primaryMarketId : _primaryMarketId,
secondaryMarketId : _secondaryMarketId,
otherAddress : _traderAddress,
otherAccountId : _toAccountId,
data : abi.encode(_amountOutMinWei)
});
}
function encodeInternalTradeActionWithCustomData(
uint256 _fromAccountId,
uint256 _toAccountId,
uint256 _primaryMarketId,
uint256 _secondaryMarketId,
address _traderAddress,
uint256 _amountInWei,
bytes memory _orderData
) internal pure returns (Actions.ActionArgs memory) {
// internal trades calculate inputAmount based on `_toAccountId` (the maker account), so the sign should be
// positive to reflect this
return Actions.ActionArgs({
actionType: Actions.ActionType.Trade,
accountId: _fromAccountId,
amount: Types.AssetAmount({
sign: true,
denomination: Types.AssetDenomination.Wei,
ref: Types.AssetReference.Delta,
value: _amountInWei
}),
primaryMarketId: _primaryMarketId,
secondaryMarketId: _secondaryMarketId,
otherAddress: _traderAddress,
otherAccountId: _toAccountId,
data: _orderData
});
}
function encodeLiquidateAction(
uint256 _solidAccountId,
uint256 _liquidAccountId,
uint256 _owedMarketId,
uint256 _heldMarketId,
uint256 _owedWeiToLiquidate
) internal pure returns (Actions.ActionArgs memory) {
return Actions.ActionArgs({
actionType: Actions.ActionType.Liquidate,
accountId: _solidAccountId,
amount: Types.AssetAmount({
sign: true,
denomination: Types.AssetDenomination.Wei,
ref: Types.AssetReference.Delta,
value: _owedWeiToLiquidate
}),
primaryMarketId: _owedMarketId,
secondaryMarketId: _heldMarketId,
otherAddress: address(0),
otherAccountId: _liquidAccountId,
data: new bytes(0)
});
}
function encodeExternalSellAction(
uint256 _fromAccountId,
uint256 _primaryMarketId,
uint256 _secondaryMarketId,
address _trader,
uint256 _amountInWei,
uint256 _amountOutMinWei,
bytes memory _orderData
) internal pure returns (Actions.ActionArgs memory) {
return Actions.ActionArgs({
actionType : Actions.ActionType.Sell,
accountId : _fromAccountId,
amount : Types.AssetAmount({
sign : false,
denomination : Types.AssetDenomination.Wei,
ref : _amountInWei == ALL ? Types.AssetReference.Target : Types.AssetReference.Delta,
value : _amountInWei == ALL ? 0 : _amountInWei
}),
primaryMarketId : _primaryMarketId,
secondaryMarketId : _secondaryMarketId,
otherAddress : _trader,
otherAccountId : 0,
data : abi.encode(_amountOutMinWei, _orderData)
});
}
function encodeTransferAction(
uint256 _fromAccountId,
uint256 _toAccountId,
uint256 _marketId,
uint256 _amountWei
) internal pure returns (Actions.ActionArgs memory) {
Types.AssetAmount memory assetAmount;
if (_amountWei == uint(- 1)) {
assetAmount = Types.AssetAmount({
sign: true,
denomination: Types.AssetDenomination.Wei,
ref: Types.AssetReference.Target,
value: 0
});
} else {
assetAmount = Types.AssetAmount({
sign: false,
denomination: Types.AssetDenomination.Wei,
ref: Types.AssetReference.Delta,
value: _amountWei
});
}
return Actions.ActionArgs({
actionType : Actions.ActionType.Transfer,
accountId : _fromAccountId,
amount : assetAmount,
primaryMarketId : _marketId,
secondaryMarketId : 0,
otherAddress : address(0),
otherAccountId : _toAccountId,
data : bytes("")
});
}
function encodeTransferToTargetAmountAction(
uint256 _fromAccountId,
uint256 _toAccountId,
uint256 _marketId,
Types.Wei memory _targetAmountWei
) internal pure returns (Actions.ActionArgs memory) {
return Actions.ActionArgs({
actionType : Actions.ActionType.Transfer,
accountId : _fromAccountId,
amount : Types.AssetAmount({
sign: _targetAmountWei.sign,
denomination: Types.AssetDenomination.Wei,
ref: Types.AssetReference.Target,
value: _targetAmountWei.value
}),
primaryMarketId : _marketId,
secondaryMarketId : 0,
otherAddress : address(0),
otherAccountId : _toAccountId,
data : bytes("")
});
}
function encodeWithdrawalAction(
uint256 _accountId,
uint256 _marketId,
Types.AssetAmount memory _amount,
address _toAccount
) internal pure returns (Actions.ActionArgs memory) {
return Actions.ActionArgs({
actionType: Actions.ActionType.Withdraw,
accountId: _accountId,
amount: _amount,
primaryMarketId: _marketId,
secondaryMarketId: 0,
otherAddress: _toAccount,
otherAccountId: 0,
data: bytes("")
});
}
}/*
Copyright 2022 Dolomite.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.7;
/**
* @title ILiquidatorAssetRegistry
* @author Dolomite
*
* Interface for a registry that tracks which assets can be liquidated and by each contract
*/
interface ILiquidatorAssetRegistry {
// ============== Events ==============
event LiquidatorAddedToWhitelist(
uint256 indexed marketId,
address indexed liquidator
);
event LiquidatorRemovedFromWhitelist(
uint256 indexed marketId,
address indexed liquidator
);
// ========== Public Functions ==========
/**
* @param _marketId The market ID of the asset
* @param _liquidator The address of the liquidator to add
*/
function ownerAddLiquidatorToAssetWhitelist(
uint256 _marketId,
address _liquidator
)
external;
/**
* @param _marketId The market ID of the asset
* @param _liquidator The address of the liquidator to remove
*/
function ownerRemoveLiquidatorFromAssetWhitelist(
uint256 _marketId,
address _liquidator
)
external;
/**
* @param _marketId The market ID of the asset to check
* @return An array of whitelisted liquidators for the asset. An empty array is returned if any liquidator can be
* used for this asset
*/
function getLiquidatorsForAsset(
uint256 _marketId
)
external view returns (address[] memory);
/**
* @param _marketId The market ID of the asset to check
* @param _liquidator The address of the liquidator to check
* @return True if the liquidator is whitelisted for the asset, false otherwise. Returns true if there
* are no whitelisted liquidators for the asset.
*/
function isAssetWhitelistedForLiquidation(
uint256 _marketId,
address _liquidator
)
external view returns (bool);
}/*
Copyright 2022 Dolomite.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.7;
pragma experimental ABIEncoderV2;
import { Actions } from "../../protocol/lib/Actions.sol";
import { IExchangeWrapper } from "../../protocol/interfaces/IExchangeWrapper.sol";
/**
* @title IIsolationModeWrapperTraderV2
* @author Dolomite
*
* Interface for a contract that can convert a token into an isolation mode token.
*/
contract IIsolationModeWrapperTraderV2 is IExchangeWrapper {
struct CreateActionsForWrappingParams {
/// @dev The index of the account (according the Accounts[] array) that is performing the sell.
uint256 primaryAccountId;
/// @dev The index of the account (according the Accounts[] array) that is being liquidated. This is set to
/// `_primaryAccountId` if a liquidation is not occurring.
uint256 otherAccountId;
/// @dev The address of the owner of the account that is performing the sell.
address primaryAccountOwner;
/// @dev The account number of the owner of the account that is performing the sell.
uint256 primaryAccountNumber;
/// @dev The address of the owner of the account that is being liquidated. This is set to
/// `_primaryAccountOwner` if a liquidation is not occurring.
address otherAccountOwner;
/// @dev The account number of the owner of the account that is being liquidated. This is set to
/// `_primaryAccountNumber` if a liquidation is not occurring.
uint256 otherAccountNumber;
/// @dev The market that is being outputted by the wrapping, should be equal to `token().
uint256 outputMarket;
/// @dev The market that is being used to wrap into `token()`.
uint256 inputMarket;
/// @dev The min amount of `_outputMarket` that must be outputted by the wrapping.
uint256 minOutputAmount;
/// @dev The amount of the `_inputMarket` that the _primaryAccountId must sell.
uint256 inputAmount;
/// @dev The calldata to pass through to any external sales that occur.
bytes orderData;
}
/**
* @return The isolation mode token that this contract can wrap (the output token)
*/
function token() external view returns (address);
/**
* @return True if the `_inputToken` is a valid input token for this contract, to be wrapped into `token()`
*/
function isValidInputToken(address _inputToken) external view returns (bool);
/**
* @return The number of Actions used to wrap a valid input token into the this wrapper's Isolation Mode token.
*/
function actionsLength() external pure returns (uint256);
/**
* @notice Creates the necessary actions for selling the `_inputMarket` into `_outputMarket`. Note, the
* `_outputMarket` should be equal to `token()` and `_inputMarket` should be validated to be a correct
* market that can be transformed into `token()`.
*
* @param _params The parameters for creating the actions for wrapping.
* @return The actions that will be executed to unwrap the `_inputMarket` into `_outputMarket`.
*/
function createActionsForWrapping(
CreateActionsForWrappingParams calldata _params
)
external
view
returns (Actions.ActionArgs[] memory);
}/*
Copyright 2022 Dolomite.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.7;
pragma experimental ABIEncoderV2;
import { Actions } from "../../protocol/lib/Actions.sol";
import { IExchangeWrapper } from "../../protocol/interfaces/IExchangeWrapper.sol";
/**
* @title IIsolationModeWrapperTrader
* @author Dolomite
*
* Interface for a contract that can convert a token into an isolation mode token.
*/
contract IIsolationModeWrapperTrader is IExchangeWrapper {
/**
* @return The isolation mode token that this contract can wrap (the output token)
*/
function token() external view returns (address);
/**
* @return True if the `_inputToken` is a valid input token for this contract, to be wrapped into `token()`
*/
function isValidInputToken(address _inputToken) external view returns (bool);
/**
* @return The number of Actions used to wrap a valid input token into the this wrapper's Isolation Mode token.
*/
function actionsLength() external pure returns (uint256);
/**
* @notice Creates the necessary actions for selling the `_inputMarket` into `_outputMarket`. Note, the
* `_outputMarket` should be equal to `token()` and `_inputMarket` should be validated to be a correct
* market that can be transformed into `token()`.
*
* @param _primaryAccountId The index of the account (according the Accounts[] array) that is performing the
* sell.
* @param _otherAccountId The index of the account (according the Accounts[] array) that is being liquidated.
* This is set to `_primaryAccountId` if a liquidation is not occurring.
* @param _primaryAccountOwner The address of the owner of the account that is performing the sell.
* @param _otherAccountOwner The address of the owner of the account that is being liquidated. This is set to
* `_primaryAccountOwner` if a liquidation is not occurring.
* @param _outputMarket The market that is being outputted by the wrapping, should be equal to `token().
* @param _inputMarket The market that is being used to wrap into `token()`.
* @param _minOutputAmount The min amount of `_outputMarket` that must be outputted by the wrapping.
* @param _inputAmount The amount of the `_inputMarket` that the _primaryAccountId must sell.
* @param _orderData The calldata to pass through to any external sales that occur.
* @return The actions that will be executed to wrap the `_inputMarket` into `_outputMarket`.
*/
function createActionsForWrapping(
uint256 _primaryAccountId,
uint256 _otherAccountId,
address _primaryAccountOwner,
address _otherAccountOwner,
uint256 _outputMarket,
uint256 _inputMarket,
uint256 _minOutputAmount,
uint256 _inputAmount,
bytes calldata _orderData
)
external
view
returns (Actions.ActionArgs[] memory);
}/*
Copyright 2022 Dolomite.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.7;
pragma experimental ABIEncoderV2;
import { Actions } from "../../protocol/lib/Actions.sol";
import { IExchangeWrapper } from "../../protocol/interfaces/IExchangeWrapper.sol";
/**
* @title IIsolationModeUnwrapperTraderV2
* @author Dolomite
*
* Interface for a contract that can convert an isolation mode token into an underlying component token.
*/
contract IIsolationModeUnwrapperTraderV2 is IExchangeWrapper {
struct CreateActionsForUnwrappingParams {
/// @dev The index of the account (according the Accounts[] array) that is performing the sell.
uint256 primaryAccountId;
/// @dev The index of the account (according the Accounts[] array) that is being liquidated. This is set to
/// `_primaryAccountId` if a liquidation is not occurring.
uint256 otherAccountId;
/// @dev The address of the owner of the account that is performing the sell.
address primaryAccountOwner;
/// @dev The account number of the owner of the account that is performing the sell.
uint256 primaryAccountNumber;
/// @dev The address of the owner of the account that is being liquidated. This is set to
/// `_primaryAccountOwner` if a liquidation is not occurring.
address otherAccountOwner;
/// @dev The account number of the owner of the account that is being liquidated. This is set to
/// `_primaryAccountNumber` if a liquidation is not occurring.
uint256 otherAccountNumber;
/// @dev The market that is being outputted by the unwrapping.
uint256 outputMarket;
/// @dev The market that is being unwrapped, should be equal to `token()`.
uint256 inputMarket;
/// @dev The min amount of `_outputMarket` that must be outputted by the unwrapping.
uint256 minOutputAmount;
/// @dev The amount of the `_inputMarket` that the _primaryAccountId must sell.
uint256 inputAmount;
/// @dev The calldata to pass through to any external sales that occur.
bytes orderData;
}
/**
* @return The isolation mode token that this contract can unwrap (the input token).
*/
function token() external view returns (address);
/**
* @return True if the `_outputToken` is a valid output token for this contract, to be unwrapped by `token()`.
*/
function isValidOutputToken(address _outputToken) external view returns (bool);
/**
* @return The number of Actions used to unwrap the liquidity token.
*/
function actionsLength() external pure returns (uint256);
/**
* @notice Creates the necessary actions for selling the `_inputMarket` into `_outputMarket`. Note, the
* `_inputMarket` should be equal to `token()` and `_outputMarket` should be validated to be a correct
* market that can be transformed into `token()`.
*
* @param _params The parameters for creating the actions for unwrapping.
* @return The actions that will be executed to unwrap the `_inputMarket` into `_outputMarket`.
*/
function createActionsForUnwrapping(
CreateActionsForUnwrappingParams calldata _params
)
external
view
returns (Actions.ActionArgs[] memory);
}/*
Copyright 2022 Dolomite.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.7;
pragma experimental ABIEncoderV2;
import { Actions } from "../../protocol/lib/Actions.sol";
import { IExchangeWrapper } from "../../protocol/interfaces/IExchangeWrapper.sol";
/**
* @title IIsolationModeUnwrapperTrader
* @author Dolomite
*
* Interface for a contract that can convert an isolation mode token into an underlying component token.
*/
contract IIsolationModeUnwrapperTrader is IExchangeWrapper {
/**
* @return The isolation mode token that this contract can unwrap (the input token).
*/
function token() external view returns (address);
/**
* @return True if the `_outputToken` is a valid output token for this contract, to be unwrapped by `token()`.
*/
function isValidOutputToken(address _outputToken) external view returns (bool);
/**
* @return The number of Actions used to unwrap the liquidity token.
*/
function actionsLength() external pure returns (uint256);
/**
* @notice Creates the necessary actions for selling the `_inputMarket` into `_outputMarket`. Note, the
* `_inputMarket` should be equal to `token()` and `_outputMarket` should be validated to be a correct
* market that can be transformed into `token()`.
*
* @param _primaryAccountId The index of the account (according the Accounts[] array) that is performing the
* sell.
* @param _otherAccountId The index of the account (according the Accounts[] array) that is being liquidated.
* This is set to `_primaryAccountId` if a liquidation is not occurring.
* @param _primaryAccountOwner The address of the owner of the account that is performing the sell.
* @param _otherAccountOwner The address of the owner of the account that is being liquidated. This is set to
* `_primaryAccountOwner` if a liquidation is not occurring.
* @param _outputMarket The market that is being outputted by the unwrapping.
* @param _inputMarket The market that is being unwrapped, should be equal to `token()`.
* @param _minOutputAmount The min amount of `_outputMarket` that must be outputted by the unwrapping.
* @param _inputAmount The amount of the `_inputMarket` that the _primaryAccountId must sell.
* @param _orderData The calldata to pass through to any external sales that occur.
* @return The actions that will be executed to unwrap the `_inputMarket` into `_outputMarket`.
*/
function createActionsForUnwrapping(
uint256 _primaryAccountId,
uint256 _otherAccountId,
address _primaryAccountOwner,
address _otherAccountOwner,
uint256 _outputMarket,
uint256 _inputMarket,
uint256 _minOutputAmount,
uint256 _inputAmount,
bytes calldata _orderData
)
external
view
returns (Actions.ActionArgs[] memory);
}/*
Copyright 2023 Dolomite.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.7;
/**
* @title IIsolationModeToken
* @author Dolomite
*
* @notice Interface for an `IsolationMode` token (implemented in Modules repository:
* https://github.com/dolomite-exchange/dolomite-margin-modules)
*/
interface IIsolationModeToken {
// ========== Public Functions ==========
/**
* @notice A token converter is used to convert this underlying token into a Dolomite-compatible one for deposit
* or withdrawal or vice-versa. Token converters are trusted contracts that are whitelisted by Dolomite,
* malicious ones would be able to mess with the accounting or misappropriate a user's funds in their proxy
* vault. Token converters can come in the form of "wrappers" or "unwrappers"
*
* @return True if the token converter is currently enabled for use by this contract.
*/
function isTokenConverterTrusted(address _tokenConverter) external view returns (bool);
/**
* @return True if the token is an isolation mode asset, false otherwise.
*/
function isIsolationAsset() external view returns (bool);
}/*
Copyright 2023 Dolomite.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.7;
pragma experimental ABIEncoderV2;
import { IDolomiteMargin } from "../../protocol/interfaces/IDolomiteMargin.sol";
import { Types } from "../../protocol/lib/Types.sol";
import {IEventEmitterRegistry} from "./IEventEmitterRegistry.sol";
/**
* @title IGenericTraderProxyBase
* @author Dolomite
*
* Base contract interface for implementing a generic trader proxy for trading assets using any trader from msg.sender
*/
interface IGenericTraderProxyBase {
// ============ Enums ============
enum TraderType {
/// @dev The trade will be conducted using external liquidity, using an `ActionType.Sell` or `ActionType.Buy`
/// action.
ExternalLiquidity,
/// @dev The trade will be conducted using internal liquidity, using an `ActionType.Trade` action.
InternalLiquidity,
/// @dev The trade will be conducted using external liquidity using an `ActionType.Sell` or `ActionType.Buy`
/// action. If this TradeType is used, the trader must be validated using
/// the `IIsolationModeToken#isTokenConverterTrusted` function on the IsolationMode token.
IsolationModeUnwrapper,
/// @dev The trade will be conducted using external liquidity using an `ActionType.Sell` or `ActionType.Buy`
/// action. If this TradeType is used, the trader must be validated using
/// the `IIsolationModeToken#isTokenConverterTrusted` function on the IsolationMode token.
IsolationModeWrapper
}
// ============ Structs ============
struct TraderParam {
/// @dev The type of trade to conduct
TraderType traderType;
/// @dev The index into the `_makerAccounts` array of the maker account to trade with. Should be set to 0 if
/// the traderType is not `TraderType.InternalLiquidity`.
uint256 makerAccountIndex;
/// @dev The address of IAutoTrader or IExchangeWrapper that will be used to conduct the trade.
address trader;
/// @dev The data that will be passed through to the trader contract.
bytes tradeData;
}
struct GenericTraderProxyCache {
IDolomiteMargin dolomiteMargin;
IEventEmitterRegistry eventEmitterRegistry;
/// @dev True if the user is making a margin deposit, false if they are withdrawing. False if the variable is
/// unused too.
bool isMarginDeposit;
/// @dev The other account number that is not `_traderAccountNumber`. Only used for TransferCollateralParams.
uint256 otherAccountNumber;
/// @dev The index into the account array at which traders start.
uint256 traderAccountStartIndex;
/// @dev The cursor for the looping through the operation's actions.
uint256 actionsCursor;
/// @dev The balance of `inputMarket` that the trader has before the call to `dolomiteMargin.operate`
Types.Wei inputBalanceWeiBeforeOperate;
/// @dev The balance of `outputMarket` that the trader has before the call to `dolomiteMargin.operate`
Types.Wei outputBalanceWeiBeforeOperate;
/// @dev The balance of `transferMarket` that the trader has before the call to `dolomiteMargin.operate`
Types.Wei transferBalanceWeiBeforeOperate;
}
}/*
Copyright 2021 Dolomite.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.7;
pragma experimental ABIEncoderV2;
import { Account } from "../../protocol/lib/Account.sol";
import { Monetary } from "../../protocol/lib/Monetary.sol";
/**
* @title IExpiry
* @author Dolomite
*/
interface IExpiry {
// ============ Enums ============
enum CallFunctionType {
SetExpiry,
SetApproval
}
// ============ Structs ============
struct SetExpiryArg {
Account.Info account;
uint256 marketId;
uint32 timeDelta;
bool forceUpdate;
}
struct SetApprovalArg {
address sender;
uint32 minTimeDelta;
}
// ============ Functions ============
function g_expiryRampTime() external view returns (uint256);
function getSpreadAdjustedPrices(
uint256 heldMarketId,
uint256 owedMarketId,
uint32 expiry
)
external
view
returns (Monetary.Price memory heldPrice, Monetary.Price memory owedPriceAdj);
function getExpiry(
Account.Info calldata account,
uint256 marketId
)
external
view
returns (uint32);
}/*
Copyright 2023 Dolomite.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.7;
pragma experimental ABIEncoderV2;
import { IGenericTraderProxyBase } from "./IGenericTraderProxyBase.sol";
import { Events } from "../../protocol/lib/Events.sol";
/**
* @title IEventEmitterRegistry
* @author Dolomite
*
* An implementation for an upgradeable proxy for emitting margin position-related events. Useful for indexing margin
* positions from a singular address.
*/
interface IEventEmitterRegistry {
// ============ Events ============
/**
* @notice This is emitted when a zap is executed
*
* @param accountOwner The address of the account that executed the zap
* @param accountNumber The sub account of the address that executed the zap
* @param marketIdsPath The path of market IDs that was executed
* @param tradersPath The path of traders that was executed
*/
event ZapExecuted(
address indexed accountOwner,
uint256 accountNumber,
uint256[] marketIdsPath,
IGenericTraderProxyBase.TraderParam[] tradersPath
);
/**
* @notice This is emitted when a borrow position is initially opened
*
* @param borrower The address of the account that opened the position
* @param borrowAccountNumber The account number of the account that opened the position
*/
event BorrowPositionOpen(
address indexed borrower,
uint256 indexed borrowAccountNumber
);
/**
* @notice This is emitted when a margin position is initially opened
*
* @param accountOwner The address of the account that opened the position
* @param accountNumber The account number of the account that opened the position
* @param inputToken The token that was sold to purchase the collateral. This should be the owed token
* @param outputToken The token that was purchased with the debt. This should be the held token
* @param depositToken The token that was deposited as collateral. This should be the held token
* @param inputBalanceUpdate The amount of inputToken that was sold to purchase the outputToken
* @param outputBalanceUpdate The amount of outputToken that was purchased with the inputToken
* @param marginDepositUpdate The amount of depositToken that was deposited as collateral
*/
event MarginPositionOpen(
address indexed accountOwner,
uint256 indexed accountNumber,
address inputToken,
address outputToken,
address depositToken,
Events.BalanceUpdate inputBalanceUpdate,
Events.BalanceUpdate outputBalanceUpdate,
Events.BalanceUpdate marginDepositUpdate
);
/**
* @notice This is emitted when a margin position is (partially) closed
*
* @param accountOwner The address of the account that opened the position
* @param accountNumber The account number of the account that opened the position
* @param inputToken The token that was sold to purchase the debt. This should be the held token
* @param outputToken The token that was purchased with the collateral. This should be the owed token
* @param withdrawalToken The token that was withdrawn as collateral. This should be the held token
* @param inputBalanceUpdate The amount of inputToken that was sold to purchase the outputToken
* @param outputBalanceUpdate The amount of outputToken that was purchased with the inputToken
* @param marginWithdrawalUpdate The amount of withdrawalToken that was deposited as collateral
*/
event MarginPositionClose(
address indexed accountOwner,
uint256 indexed accountNumber,
address inputToken,
address outputToken,
address withdrawalToken,
Events.BalanceUpdate inputBalanceUpdate,
Events.BalanceUpdate outputBalanceUpdate,
Events.BalanceUpdate marginWithdrawalUpdate
);
// ============ Functions ============
/**
* @notice Emits a ZapExecuted event
*
* @param _accountOwner The address of the account that executed the zap
* @param _accountNumber The sub account of the address that executed the zap
* @param _marketIdsPath The path of market IDs that was executed
* @param _tradersPath The path of traders that was executed
*/
function emitZapExecuted(
address _accountOwner,
uint256 _accountNumber,
uint256[] calldata _marketIdsPath,
IGenericTraderProxyBase.TraderParam[] calldata _tradersPath
)
external;
/**
* @notice Emits a MarginPositionOpen event
*
* @param _accountOwner The address of the account that opened the position
* @param _accountNumber The account number of the account that opened the position
*/
function emitBorrowPositionOpen(
address _accountOwner,
uint256 _accountNumber
)
external;
/**
* @notice Emits a MarginPositionOpen event
*
* @param _accountOwner The address of the account that opened the position
* @param _accountNumber The account number of the account that opened the position
* @param _inputToken The token that was sold to purchase the collateral. This should be the owed token
* @param _outputToken The token that was purchased with the debt. This should be the held token
* @param _depositToken The token that was deposited as collateral. This should be the held token
* @param _inputBalanceUpdate The amount of inputToken that was sold to purchase the outputToken
* @param _outputBalanceUpdate The amount of outputToken that was purchased with the inputToken
* @param _marginDepositUpdate The amount of depositToken that was deposited as collateral
*/
function emitMarginPositionOpen(
address _accountOwner,
uint256 _accountNumber,
address _inputToken,
address _outputToken,
address _depositToken,
Events.BalanceUpdate calldata _inputBalanceUpdate,
Events.BalanceUpdate calldata _outputBalanceUpdate,
Events.BalanceUpdate calldata _marginDepositUpdate
)
external;
/**
* @notice Emits a MarginPositionClose event
*
* @param _accountOwner The address of the account that opened the position
* @param _accountNumber The account number of the account that opened the position
* @param _inputToken The token that was sold to purchase the debt. This should be the held token
* @param _outputToken The token that was purchased with the collateral. This should be the owed token
* @param _withdrawalToken The token that was withdrawn as collateral. This should be the held token
* @param _inputBalanceUpdate The amount of inputToken that was sold to purchase the outputToken
* @param _outputBalanceUpdate The amount of outputToken that was purchased with the inputToken
* @param _marginWithdrawalUpdate The amount of withdrawalToken that was deposited as collateral
*/
function emitMarginPositionClose(
address _accountOwner,
uint256 _accountNumber,
address _inputToken,
address _outputToken,
address _withdrawalToken,
Events.BalanceUpdate calldata _inputBalanceUpdate,
Events.BalanceUpdate calldata _outputBalanceUpdate,
Events.BalanceUpdate calldata _marginWithdrawalUpdate
)
external;
}/*
Copyright 2022 Dolomite.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.7;
pragma experimental ABIEncoderV2;
import { IDolomiteMargin } from "../../protocol/interfaces/IDolomiteMargin.sol";
import { Require } from "../../protocol/lib/Require.sol";
/**
* @title OnlyDolomiteMargin
* @author Dolomite
*
* Inheritable contract that restricts the calling of certain functions to DolomiteMargin only
*/
contract OnlyDolomiteMargin {
// ============ Constants ============
bytes32 private constant FILE = "OnlyDolomiteMargin";
// ============ Storage ============
IDolomiteMargin public DOLOMITE_MARGIN;
// ============ Constructor ============
constructor (
address _dolomiteMargin
)
public
{
DOLOMITE_MARGIN = IDolomiteMargin(_dolomiteMargin);
}
// ============ Modifiers ============
modifier onlyDolomiteMargin(address _from) {
Require.that(
_from == address(DOLOMITE_MARGIN),
FILE,
"Only Dolomite can call function",
_from
);
_;
}
modifier onlyDolomiteMarginOwner(address _from) {
Require.that(
_from == DOLOMITE_MARGIN.owner(),
FILE,
"Only Dolomite owner can call",
_from
);
_;
}
modifier onlyGlobalOperator(address _from) {
Require.that(
DOLOMITE_MARGIN.getIsGlobalOperator(_from),
FILE,
"Only global operator can call",
_from
);
_;
}
}/*
Copyright 2022 Dolomite.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.7;
pragma experimental ABIEncoderV2;
import { SafeMath } from "@openzeppelin/contracts/math/SafeMath.sol";
import { IDolomiteMargin } from "../../protocol/interfaces/IDolomiteMargin.sol";
import { IExpiry } from "../interfaces/IExpiry.sol";
import { ILiquidatorAssetRegistry } from "../interfaces/ILiquidatorAssetRegistry.sol";
import { Account } from "../../protocol/lib/Account.sol";
import { Bits } from "../../protocol/lib/Bits.sol";
import { Decimal } from "../../protocol/lib/Decimal.sol";
import { DolomiteMarginMath } from "../../protocol/lib/DolomiteMarginMath.sol";
import { Interest } from "../../protocol/lib/Interest.sol";
import { Monetary } from "../../protocol/lib/Monetary.sol";
import { Require } from "../../protocol/lib/Require.sol";
import { Time } from "../../protocol/lib/Time.sol";
import { Types } from "../../protocol/lib/Types.sol";
import { HasLiquidatorRegistry } from "./HasLiquidatorRegistry.sol";
/**
* @title LiquidatorProxyBase
* @author Dolomite
*
* Inheritable contract that allows sharing code across different liquidator proxy contracts
*/
contract LiquidatorProxyBase is HasLiquidatorRegistry {
using SafeMath for uint;
using Types for Types.Par;
// ============ Constants ============
bytes32 private constant FILE = "LiquidatorProxyBase";
uint256 private constant MAX_UINT_BITS = 256;
uint256 private constant ONE = 1;
// ============ Structs ============
struct MarketInfo {
uint256 marketId;
Monetary.Price price;
Interest.Index index;
}
// ============ Structs ============
struct LiquidatorProxyConstants {
IDolomiteMargin dolomiteMargin;
Account.Info solidAccount;
Account.Info liquidAccount;
uint256 heldMarket;
uint256 owedMarket;
MarketInfo[] markets;
uint256[] liquidMarkets;
IExpiry expiryProxy;
uint32 expiry;
}
struct LiquidatorProxyCache {
// mutable
uint256 owedWeiToLiquidate;
// The amount of heldMarket the solidAccount will receive. Includes the liquidation reward. Useful as the
// `amountIn` for a trade
uint256 solidHeldUpdateWithReward;
Types.Wei solidHeldWei;
Types.Wei solidOwedWei;
Types.Wei liquidHeldWei;
Types.Wei liquidOwedWei;
// This exists purely for expirations. If the amount being repaid is meant to be ALL but the value of the debt
// is greater than the value of the collateral, then we need to flip the markets in the trade for the Target=0
// encoding of the Amount. There's a rounding issue otherwise because amounts are calculated differently for
// trades vs. liquidations
bool flipMarketsForExpiration;
// immutable
uint256 heldPrice;
uint256 owedPrice;
uint256 owedPriceAdj;
}
// ============ Internal Functions ============
/**
* Pre-populates cache values for some pair of markets.
*/
function _initializeCache(
LiquidatorProxyConstants memory _constants
)
internal
view
returns (LiquidatorProxyCache memory)
{
MarketInfo memory heldMarketInfo = _binarySearch(_constants.markets, _constants.heldMarket);
MarketInfo memory owedMarketInfo = _binarySearch(_constants.markets, _constants.owedMarket);
uint256 owedPriceAdj;
if (_constants.expiry > 0) {
(, Monetary.Price memory owedPricePrice) = _constants.expiryProxy.getSpreadAdjustedPrices(
_constants.heldMarket,
_constants.owedMarket,
_constants.expiry
);
owedPriceAdj = owedPricePrice.value;
} else {
Decimal.D256 memory spread = _constants.dolomiteMargin.getLiquidationSpreadForPair(
_constants.heldMarket,
_constants.owedMarket
);
owedPriceAdj = owedMarketInfo.price.value.add(Decimal.mul(owedMarketInfo.price.value, spread));
}
return LiquidatorProxyCache({
owedWeiToLiquidate: 0,
solidHeldUpdateWithReward: 0,
solidHeldWei: Interest.parToWei(
_constants.dolomiteMargin.getAccountPar(_constants.solidAccount, _constants.heldMarket),
heldMarketInfo.index
),
solidOwedWei: Interest.parToWei(
_constants.dolomiteMargin.getAccountPar(_constants.solidAccount, _constants.owedMarket),
owedMarketInfo.index
),
liquidHeldWei: Interest.parToWei(
_constants.dolomiteMargin.getAccountPar(_constants.liquidAccount, _constants.heldMarket),
heldMarketInfo.index
),
liquidOwedWei: Interest.parToWei(
_constants.dolomiteMargin.getAccountPar(_constants.liquidAccount, _constants.owedMarket),
owedMarketInfo.index
),
flipMarketsForExpiration: false,
heldPrice: heldMarketInfo.price.value,
owedPrice: owedMarketInfo.price.value,
owedPriceAdj: owedPriceAdj
});
}
/**
* Make some basic checks before attempting to liquidate an account.
* - Require that the msg.sender has the permission to use the liquidator account
* - Require that the liquid account is liquidatable based on the accounts global value (all assets held and owed,
* not just what's being liquidated)
*/
function _checkConstants(
LiquidatorProxyConstants memory _constants,
uint256 _expiry
)
internal
view
{
// panic if the developer didn't set these variables already
assert(address(_constants.dolomiteMargin) != address(0));
assert(_constants.solidAccount.owner != address(0));
assert(_constants.liquidAccount.owner != address(0));
Require.that(
_constants.owedMarket != _constants.heldMarket,
FILE,
"Owed market equals held market",
_constants.owedMarket
);
Require.that(
!_constants.dolomiteMargin.getAccountPar(_constants.liquidAccount, _constants.owedMarket).isPositive(),
FILE,
"Owed market cannot be positive",
_constants.owedMarket
);
Require.that(
_constants.dolomiteMargin.getAccountPar(_constants.liquidAccount, _constants.heldMarket).isPositive(),
FILE,
"Held market cannot be negative",
_constants.heldMarket
);
Require.that(
uint32(_expiry) == _expiry,
FILE,
"Expiry overflows",
_expiry
);
Require.that(
_expiry <= Time.currentTime(),
FILE,
"Borrow not yet expired",
_expiry
);
}
/**
* Make some basic checks before attempting to liquidate an account.
* - Require that the msg.sender has the permission to use the solid account
* - Require that the liquid account is liquidatable if using an expiry
*/
function _checkBasicRequirements(
LiquidatorProxyConstants memory _constants
)
internal
view
{
// check credentials for msg.sender
Require.that(
_constants.solidAccount.owner == msg.sender
|| _constants.dolomiteMargin.getIsLocalOperator(_constants.solidAccount.owner, msg.sender),
FILE,
"Sender not operator",
msg.sender
);
if (_constants.expiry != 0) {
// check the expiration is valid; to get here we already know constants.expiry != 0
uint32 expiry = _constants.expiryProxy.getExpiry(_constants.liquidAccount, _constants.owedMarket);
Require.that(
expiry == _constants.expiry,
FILE,
"Expiry mismatch",
expiry,
_constants.expiry
);
}
}
/**
* Calculate the maximum amount that can be liquidated on `liquidAccount`
*/
function _calculateAndSetMaxLiquidationAmount(
LiquidatorProxyCache memory _cache
)
internal
pure
{
uint256 liquidHeldValue = _cache.heldPrice.mul(_cache.liquidHeldWei.value);
uint256 liquidOwedValue = _cache.owedPriceAdj.mul(_cache.liquidOwedWei.value);
if (liquidHeldValue < liquidOwedValue) {
// The held collateral is worth less than the adjusted debt
_cache.solidHeldUpdateWithReward = _cache.liquidHeldWei.value;
_cache.owedWeiToLiquidate = DolomiteMarginMath.getPartialRoundUp(
_cache.liquidHeldWei.value,
_cache.heldPrice,
_cache.owedPriceAdj
);
_cache.flipMarketsForExpiration = true;
} else {
_cache.solidHeldUpdateWithReward = DolomiteMarginMath.getPartial(
_cache.liquidOwedWei.value,
_cache.owedPriceAdj,
_cache.heldPrice
);
_cache.owedWeiToLiquidate = _cache.liquidOwedWei.value;
}
}
function _calculateAndSetActualLiquidationAmount(
uint256 _minOutputAmountWei,
LiquidatorProxyCache memory _cache
)
internal
pure
returns (uint256 _newMinOutputAmountWei)
{
// at this point, _cache.owedWeiToLiquidate should be the max amount that can be liquidated on the user.
assert(_cache.owedWeiToLiquidate > 0); // assert it was initialized
_newMinOutputAmountWei = _minOutputAmountWei;
if (_newMinOutputAmountWei == uint256(-1)) {
_newMinOutputAmountWei = _cache.owedWeiToLiquidate;
} else if (_newMinOutputAmountWei < _cache.owedWeiToLiquidate) {
_cache.owedWeiToLiquidate = _newMinOutputAmountWei;
_cache.solidHeldUpdateWithReward = DolomiteMarginMath.getPartial(
_newMinOutputAmountWei,
_cache.owedPriceAdj,
_cache.heldPrice
);
}
}
/**
* Returns true if the supplyValue over-collateralizes the borrowValue by the ratio.
*/
function _isCollateralized(
uint256 supplyValue,
uint256 borrowValue,
Decimal.D256 memory ratio
)
internal
pure
returns (bool)
{
uint256 requiredMargin = Decimal.mul(borrowValue, ratio);
return supplyValue >= borrowValue.add(requiredMargin);
}
/**
* Gets the current total supplyValue and borrowValue for some account. Takes into account what
* the current index will be once updated.
*/
function _getAccountValues(
IDolomiteMargin dolomiteMargin,
MarketInfo[] memory marketInfos,
Account.Info memory account,
uint256[] memory marketIds
)
internal
view
returns (
Monetary.Value memory,
Monetary.Value memory
)
{
return _getAccountValues(
dolomiteMargin,
marketInfos,
account,
marketIds,
/* adjustForMarginPremiums = */ false // solium-disable-line indentation
);
}
/**
* Gets the adjusted current total supplyValue and borrowValue for some account. Takes into account what
* the current index will be once updated and the margin premium.
*/
function _getAdjustedAccountValues(
IDolomiteMargin dolomiteMargin,
MarketInfo[] memory marketInfos,
Account.Info memory account,
uint256[] memory marketIds
)
internal
view
returns (
Monetary.Value memory,
Monetary.Value memory
)
{
return _getAccountValues(
dolomiteMargin,
marketInfos,
account,
marketIds,
/* adjustForMarginPremiums = */ true // solium-disable-line indentation
);
}
function _getMarketInfos(
IDolomiteMargin dolomiteMargin,
uint256[] memory solidMarkets,
uint256[] memory liquidMarkets
) internal view returns (MarketInfo[] memory) {
uint256[] memory marketBitmaps = Bits.createBitmaps(dolomiteMargin.getNumMarkets());
uint256 marketsLength = 0;
marketsLength = _addMarketsToBitmap(solidMarkets, marketBitmaps, marketsLength);
marketsLength = _addMarketsToBitmap(liquidMarkets, marketBitmaps, marketsLength);
uint256 counter = 0;
MarketInfo[] memory marketInfos = new MarketInfo[](marketsLength);
for (uint256 i = 0; i < marketBitmaps.length; i++) {
uint256 bitmap = marketBitmaps[i];
while (bitmap != 0) {
uint256 nextSetBit = Bits.getLeastSignificantBit(bitmap);
uint256 marketId = Bits.getMarketIdFromBit(i, nextSetBit);
marketInfos[counter++] = MarketInfo({
marketId: marketId,
price: dolomiteMargin.getMarketPrice(marketId),
index: dolomiteMargin.getMarketCurrentIndex(marketId)
});
// unset the set bit
bitmap = Bits.unsetBit(bitmap, nextSetBit);
}
if (counter == marketsLength) {
break;
}
}
return marketInfos;
}
function _binarySearch(
MarketInfo[] memory markets,
uint256 marketId
) internal pure returns (MarketInfo memory) {
return _binarySearch(
markets,
0,
markets.length,
marketId
);
}
// ============ Private Functions ============
function _getAccountValues(
IDolomiteMargin dolomiteMargin,
MarketInfo[] memory marketInfos,
Account.Info memory account,
uint256[] memory marketIds,
bool adjustForMarginPremiums
)
private
view
returns (
Monetary.Value memory,
Monetary.Value memory
)
{
Monetary.Value memory supplyValue;
Monetary.Value memory borrowValue;
for (uint256 i = 0; i < marketIds.length; i++) {
Types.Par memory par = dolomiteMargin.getAccountPar(account, marketIds[i]);
MarketInfo memory marketInfo = _binarySearch(marketInfos, marketIds[i]);
Types.Wei memory userWei = Interest.parToWei(par, marketInfo.index);
uint256 assetValue = userWei.value.mul(marketInfo.price.value);
Decimal.D256 memory marginPremium = Decimal.one();
if (adjustForMarginPremiums) {
marginPremium = Decimal.onePlus(dolomiteMargin.getMarketMarginPremium(marketIds[i]));
}
if (userWei.sign) {
supplyValue.value = supplyValue.value.add(Decimal.div(assetValue, marginPremium));
} else {
borrowValue.value = borrowValue.value.add(Decimal.mul(assetValue, marginPremium));
}
}
return (supplyValue, borrowValue);
}
// solium-disable-next-line security/no-assign-params
function _addMarketsToBitmap(
uint256[] memory markets,
uint256[] memory bitmaps,
uint256 marketsLength
) private pure returns (uint) {
for (uint256 i = 0; i < markets.length; i++) {
if (!Bits.hasBit(bitmaps, markets[i])) {
Bits.setBit(bitmaps, markets[i]);
marketsLength += 1;
}
}
return marketsLength;
}
function _binarySearch(
MarketInfo[] memory markets,
uint256 beginInclusive,
uint256 endExclusive,
uint256 marketId
) private pure returns (MarketInfo memory) {
uint256 len = endExclusive - beginInclusive;
if (len == 0 || (len == 1 && markets[beginInclusive].marketId != marketId)) {
revert("LiquidatorProxyBase: Market not found");
}
uint256 mid = beginInclusive + len / 2;
uint256 midMarketId = markets[mid].marketId;
if (marketId < midMarketId) {
return _binarySearch(
markets,
beginInclusive,
mid,
marketId
);
} else if (marketId > midMarketId) {
return _binarySearch(
markets,
mid + 1,
endExclusive,
marketId
);
} else {
return markets[mid];
}
}
}/*
Copyright 2022 Dolomite.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.7;
import { Require } from "../../protocol/lib/Require.sol";
import { ILiquidatorAssetRegistry } from "../interfaces/ILiquidatorAssetRegistry.sol";
contract HasLiquidatorRegistry {
// ============ Constants ============
bytes32 private constant FILE = "HasLiquidatorRegistry";
// ============ Storage ============
ILiquidatorAssetRegistry public LIQUIDATOR_ASSET_REGISTRY;
// ============ Modifiers ============
modifier requireIsAssetWhitelistedForLiquidation(uint256 _marketId) {
_validateAssetForLiquidation(_marketId);
_;
}
modifier requireIsAssetsWhitelistedForLiquidation(uint256[] memory _marketIds) {
_validateAssetsForLiquidation(_marketIds);
_;
}
// ============ Constructors ============
constructor(
address _liquidatorAssetRegistry
)
public
{
LIQUIDATOR_ASSET_REGISTRY = ILiquidatorAssetRegistry(_liquidatorAssetRegistry);
}
// ============ Internal Functions ============
function _validateAssetForLiquidation(uint256 _marketId) internal view {
Require.that(
LIQUIDATOR_ASSET_REGISTRY.isAssetWhitelistedForLiquidation(_marketId, address(this)),
FILE,
"Asset not whitelisted",
_marketId
);
}
function _validateAssetsForLiquidation(uint256[] memory _marketIds) internal view {
ILiquidatorAssetRegistry liquidatorAssetRegistry = LIQUIDATOR_ASSET_REGISTRY;
for (uint256 i = 0; i < _marketIds.length; i++) {
Require.that(
liquidatorAssetRegistry.isAssetWhitelistedForLiquidation(_marketIds[i], address(this)),
FILE,
"Asset not whitelisted",
_marketIds[i]
);
}
}
}/*
Copyright 2023 Dolomite.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.7;
pragma experimental ABIEncoderV2;
import { ReentrancyGuard } from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import { IDolomiteMargin } from "../../protocol/interfaces/IDolomiteMargin.sol";
import { IERC20Detailed } from "../../protocol/interfaces/IERC20Detailed.sol";
import { Account } from "../../protocol/lib/Account.sol";
import { Actions } from "../../protocol/lib/Actions.sol";
import { Events } from "../../protocol/lib/Events.sol";
import { ExcessivelySafeCall } from "../../protocol/lib/ExcessivelySafeCall.sol";
import { Require } from "../../protocol/lib/Require.sol";
import { OnlyDolomiteMargin } from "../helpers/OnlyDolomiteMargin.sol";
import { IExpiry } from "../interfaces/IExpiry.sol";
import { IGenericTraderProxyBase } from "../interfaces/IGenericTraderProxyBase.sol";
import { IIsolationModeToken } from "../interfaces/IIsolationModeToken.sol";
import { IIsolationModeUnwrapperTrader } from "../interfaces/IIsolationModeUnwrapperTrader.sol";
import { IIsolationModeUnwrapperTraderV2 } from "../interfaces/IIsolationModeUnwrapperTraderV2.sol";
import { IIsolationModeWrapperTrader } from "../interfaces/IIsolationModeWrapperTrader.sol";
import { IIsolationModeWrapperTraderV2 } from "../interfaces/IIsolationModeWrapperTraderV2.sol";
import { AccountActionLib } from "../lib/AccountActionLib.sol";
/**
* @title GenericTraderProxyBase
* @author Dolomite
*
* @dev Base contract with validation and utilities for trading any asset from an account
*/
contract GenericTraderProxyBase is IGenericTraderProxyBase {
// ============ Constants ============
bytes32 private constant FILE = "GenericTraderProxyBase";
/// @dev The index of the trade account in the accounts array (for executing an operation)
uint256 internal constant TRADE_ACCOUNT_ID = 0;
uint256 internal constant ZAP_ACCOUNT_ID = 1;
bytes32 internal constant GLP_ISOLATION_MODE_HASH = keccak256(bytes("Dolomite: Fee + Staked GLP"));
bytes32 internal constant ISOLATION_MODE_PREFIX_HASH = keccak256(bytes("Dolomite Isolation:"));
uint256 internal constant DOLOMITE_ISOLATION_LENGTH = 19;
// ============ Public Functions ============
function isIsolationModeMarket(
IDolomiteMargin _dolomiteMargin,
uint256 _marketId
) public view returns (bool) {
(bool isSuccess, bytes memory returnData) = ExcessivelySafeCall.safeStaticCall(
_dolomiteMargin.getMarketTokenAddress(_marketId),
IERC20Detailed(address(0)).name.selector,
bytes("")
);
if (!isSuccess) {
return false;
}
string memory name = abi.decode(returnData, (string));
return (
(bytes(name).length >= DOLOMITE_ISOLATION_LENGTH
&& _hashSubstring(
name,
/* _startIndex = */ 0, // solium-disable-line indentation
/* _endIndex = */ DOLOMITE_ISOLATION_LENGTH // solium-disable-line indentation
) == ISOLATION_MODE_PREFIX_HASH
)
|| keccak256(bytes(name)) == GLP_ISOLATION_MODE_HASH
);
}
// ============ Internal Functions ============
function _validateMarketIdPath(
uint256[] memory _marketIdsPath
) internal pure {
Require.that(
_marketIdsPath.length >= 2,
FILE,
"Invalid market path length"
);
}
function _validateAmountWeis(
uint256 _inputAmountWei,
uint256 _minOutputAmountWei
)
internal
pure
{
Require.that(
_inputAmountWei > 0,
FILE,
"Invalid inputAmountWei"
);
Require.that(
_minOutputAmountWei > 0,
FILE,
"Invalid minOutputAmountWei"
);
}
function _validateTraderParams(
GenericTraderProxyCache memory _cache,
uint256[] memory _marketIdsPath,
Account.Info[] memory _makerAccounts,
TraderParam[] memory _traderParamsPath
)
internal
view
{
Require.that(
_marketIdsPath.length == _traderParamsPath.length + 1,
FILE,
"Invalid traders params length"
);
for (uint256 i = 0; i < _traderParamsPath.length; i++) {
_validateTraderParam(
_cache,
_marketIdsPath,
_makerAccounts,
_traderParamsPath[i],
/* _index = */ i // solium-disable-line indentation
);
}
}
function _validateTraderParam(
GenericTraderProxyCache memory _cache,
uint256[] memory _marketIdsPath,
Account.Info[] memory _makerAccounts,
TraderParam memory _traderParam,
uint256 _index
)
internal
view
{
Require.that(
_traderParam.trader != address(0),
FILE,
"Invalid trader at index",
_index
);
uint256 marketId = _marketIdsPath[_index];
uint256 nextMarketId = _marketIdsPath[_index + 1];
_validateIsolationModeStatusForTraderParam(
_cache,
marketId,
nextMarketId,
_traderParam
);
_validateTraderTypeForTraderParam(
_cache,
marketId,
nextMarketId,
_traderParam,
_index
);
_validateMakerAccountForTraderParam(
_makerAccounts,
_traderParam,
_index
);
}
function _validateIsolationModeStatusForTraderParam(
GenericTraderProxyCache memory _cache,
uint256 _marketId,
uint256 _nextMarketId,
TraderParam memory _traderParam
) internal view {
if (isIsolationModeMarket(_cache.dolomiteMargin, _marketId)) {
// If the current market is in isolation mode, the trader type must be for isolation mode assets
Require.that(
_isUnwrapperTraderType(_traderParam.traderType),
FILE,
"Invalid isolation mode unwrapper",
_marketId,
uint256(uint8(_traderParam.traderType))
);
if (isIsolationModeMarket(_cache.dolomiteMargin, _nextMarketId)) {
// If the user is unwrapping into an isolation mode asset, the next market must trust this trader
address isolationModeToken = _cache.dolomiteMargin.getMarketTokenAddress(_nextMarketId);
Require.that(
IIsolationModeToken(isolationModeToken).isTokenConverterTrusted(_traderParam.trader),
FILE,
"Invalid unwrap sequence",
_marketId,
_nextMarketId
);
}
} else if (isIsolationModeMarket(_cache.dolomiteMargin, _nextMarketId)) {
// If the next market is in isolation mode, the trader must wrap the current asset into the isolation asset.
Require.that(
_isWrapperTraderType(_traderParam.traderType),
FILE,
"Invalid isolation mode wrapper",
_nextMarketId,
uint256(uint8(_traderParam.traderType))
);
} else {
// If neither asset is in isolation mode, the trader type must be for non-isolation mode assets
Require.that(
_traderParam.traderType == TraderType.ExternalLiquidity
|| _traderParam.traderType == TraderType.InternalLiquidity,
FILE,
"Invalid trader type",
uint256(uint8(_traderParam.traderType))
);
}
}
function _validateTraderTypeForTraderParam(
GenericTraderProxyCache memory _cache,
uint256 _marketId,
uint256 _nextMarketId,
TraderParam memory _traderParam,
uint256 _index
) internal view {
if (_isUnwrapperTraderType(_traderParam.traderType)) {
IIsolationModeUnwrapperTrader unwrapperTrader = IIsolationModeUnwrapperTrader(_traderParam.trader);
address isolationModeToken = _cache.dolomiteMargin.getMarketTokenAddress(_marketId);
Require.that(
unwrapperTrader.token() == isolationModeToken,
FILE,
"Invalid input for unwrapper",
_index,
_marketId
);
Require.that(
unwrapperTrader.isValidOutputToken(_cache.dolomiteMargin.getMarketTokenAddress(_nextMarketId)),
FILE,
"Invalid output for unwrapper",
_index + 1,
_nextMarketId
);
Require.that(
IIsolationModeToken(isolationModeToken).isTokenConverterTrusted(_traderParam.trader),
FILE,
"Unwrapper trader not enabled",
_traderParam.trader,
_marketId
);
} else if (_isWrapperTraderType(_traderParam.traderType)) {
IIsolationModeWrapperTrader wrapperTrader = IIsolationModeWrapperTrader(_traderParam.trader);
address isolationModeToken = _cache.dolomiteMargin.getMarketTokenAddress(_nextMarketId);
Require.that(
wrapperTrader.isValidInputToken(_cache.dolomiteMargin.getMarketTokenAddress(_marketId)),
FILE,
"Invalid input for wrapper",
_index,
_marketId
);
Require.that(
wrapperTrader.token() == isolationModeToken,
FILE,
"Invalid output for wrapper",
_index + 1,
_nextMarketId
);
Require.that(
IIsolationModeToken(isolationModeToken).isTokenConverterTrusted(_traderParam.trader),
FILE,
"Wrapper trader not enabled",
_traderParam.trader,
_nextMarketId
);
}
}
function _validateMakerAccountForTraderParam(
Account.Info[] memory _makerAccounts,
TraderParam memory _traderParam,
uint256 _index
) internal pure {
if (TraderType.InternalLiquidity == _traderParam.traderType) {
// The makerAccountOwner should be set if the traderType is InternalLiquidity
Require.that(
_traderParam.makerAccountIndex < _makerAccounts.length
&& _makerAccounts[_traderParam.makerAccountIndex].owner != address(0),
FILE,
"Invalid maker account owner",
_index
);
} else {
// The makerAccountOwner and makerAccountNumber is not used if the traderType is not InternalLiquidity
Require.that(
_traderParam.makerAccountIndex == 0,
FILE,
"Invalid maker account owner",
_index
);
}
}
function _validateZapAccount(
GenericTraderProxyCache memory _cache,
Account.Info memory _account,
uint256[] memory _marketIdsPath
) internal view {
for (uint256 i = 0; i < _marketIdsPath.length; i++) {
// Panic if we're zapping to an account that has any value in it. Why? Because we don't want execute trades
// where we sell ALL if there's already value in the account. That would mess up the user's holdings and
// unintentionally sell assets the user does not want to sell.
assert(_cache.dolomiteMargin.getAccountPar(_account, _marketIdsPath[i]).value == 0);
}
}
function _getAccounts(
GenericTraderProxyCache memory _cache,
Account.Info[] memory _makerAccounts,
address _tradeAccountOwner,
uint256 _tradeAccountNumber
)
internal
view
returns (Account.Info[] memory)
{
Account.Info[] memory accounts = new Account.Info[](_cache.traderAccountStartIndex + _makerAccounts.length);
accounts[TRADE_ACCOUNT_ID] = Account.Info({
owner: _tradeAccountOwner,
number: _tradeAccountNumber
});
accounts[ZAP_ACCOUNT_ID] = Account.Info({
owner: _tradeAccountOwner,
number: _calculateZapAccountNumber(_tradeAccountOwner, _tradeAccountNumber)
});
_appendTradersToAccounts(_cache, _makerAccounts, accounts);
return accounts;
}
function _appendTradersToAccounts(
GenericTraderProxyCache memory _cache,
Account.Info[] memory _makerAccounts,
Account.Info[] memory _accounts
)
internal
pure
{
for (uint256 i = 0; i < _makerAccounts.length; i++) {
Account.Info memory account = _accounts[_cache.traderAccountStartIndex + i];
assert(account.owner == address(0) && account.number == 0);
_accounts[_cache.traderAccountStartIndex + i] = Account.Info({
owner: _makerAccounts[i].owner,
number: _makerAccounts[i].number
});
}
}
function _getActionsLengthForTraderParams(
TraderParam[] memory _tradersPath
)
internal
pure
returns (uint256)
{
uint256 actionsLength = 2; // start at 2 for the zap in/out of the zap account (2 transfer actions)
for (uint256 i = 0; i < _tradersPath.length; i++) {
if (_isUnwrapperTraderType(_tradersPath[i].traderType)) {
actionsLength += IIsolationModeUnwrapperTrader(_tradersPath[i].trader).actionsLength();
} else if (_isWrapperTraderType(_tradersPath[i].traderType)) {
actionsLength += IIsolationModeWrapperTrader(_tradersPath[i].trader).actionsLength();
} else {
actionsLength += 1;
}
}
return actionsLength;
}
function _appendTraderActions(
Account.Info[] memory _accounts,
Actions.ActionArgs[] memory _actions,
GenericTraderProxyCache memory _cache,
uint256[] memory _marketIdsPath,
uint256 _inputAmountWei,
uint256 _minOutputAmountWei,
TraderParam[] memory _tradersPath
)
internal
view
{
// Before the trades are started, transfer inputAmountWei of the inputMarket from the TRADE account to the ZAP account
if (_inputAmountWei == uint256(-1)) {
// Transfer such that we TARGET w/e the trader has right now, before the trades occur
_actions[_cache.actionsCursor++] = AccountActionLib.encodeTransferToTargetAmountAction(
TRADE_ACCOUNT_ID,
ZAP_ACCOUNT_ID,
_marketIdsPath[0],
/* _targetAmountWei = */ _cache.dolomiteMargin.getAccountWei(
_accounts[TRADE_ACCOUNT_ID],
_marketIdsPath[0]
)
);
} else {
_actions[_cache.actionsCursor++] = AccountActionLib.encodeTransferAction(
TRADE_ACCOUNT_ID,
ZAP_ACCOUNT_ID,
_marketIdsPath[0],
_inputAmountWei
);
}
for (uint256 i = 0; i < _tradersPath.length; i++) {
if (_tradersPath[i].traderType == TraderType.ExternalLiquidity) {
_actions[_cache.actionsCursor++] = AccountActionLib.encodeExternalSellAction(
ZAP_ACCOUNT_ID,
_marketIdsPath[i],
_marketIdsPath[i + 1],
_tradersPath[i].trader,
_getInputAmountWeiForIndex(_inputAmountWei, i),
_getMinOutputAmountWeiForIndex(_minOutputAmountWei, i, _tradersPath.length),
_tradersPath[i].tradeData
);
} else if (_tradersPath[i].traderType == TraderType.InternalLiquidity) {
(
uint256 customInputAmountWei,
bytes memory tradeData
) = abi.decode(_tradersPath[i].tradeData, (uint256, bytes));
Require.that(
(i == 0 && customInputAmountWei == _inputAmountWei) || i != 0,
FILE,
"Invalid custom input amount"
);
_actions[_cache.actionsCursor++] = AccountActionLib.encodeInternalTradeActionWithCustomData(
ZAP_ACCOUNT_ID,
/* _makerAccountId = */ _tradersPath[i].makerAccountIndex + _cache.traderAccountStartIndex,
_marketIdsPath[i],
_marketIdsPath[i + 1],
_tradersPath[i].trader,
customInputAmountWei,
tradeData
);
} else if (_isUnwrapperTraderType(_tradersPath[i].traderType)) {
// We can't use a Require for the following assert, because there's already an invariant that enforces
// the trader is an `IsolationModeWrapper` if the market ID at `i + 1` is in isolation mode. Meaning,
// an unwrapper can never appear at the non-zero index because there is an invariant that checks the
// `IsolationModeWrapper` is the last index
assert(i == 0);
Actions.ActionArgs[] memory unwrapperActions = IIsolationModeUnwrapperTraderV2(_tradersPath[i].trader)
.createActionsForUnwrapping(
IIsolationModeUnwrapperTraderV2.CreateActionsForUnwrappingParams({
primaryAccountId: ZAP_ACCOUNT_ID,
otherAccountId: _otherAccountId(),
primaryAccountOwner: _accounts[ZAP_ACCOUNT_ID].owner,
primaryAccountNumber: _accounts[ZAP_ACCOUNT_ID].number,
otherAccountOwner: _accounts[_otherAccountId()].owner,
otherAccountNumber: _accounts[_otherAccountId()].number,
outputMarket: _marketIdsPath[i + 1],
inputMarket: _marketIdsPath[i],
minOutputAmount: _getMinOutputAmountWeiForIndex(_minOutputAmountWei, i, _tradersPath.length),
inputAmount: _getInputAmountWeiForIndex(_inputAmountWei, i),
orderData: _tradersPath[i].tradeData
})
);
for (uint256 j = 0; j < unwrapperActions.length; j++) {
_actions[_cache.actionsCursor++] = unwrapperActions[j];
}
} else {
// Panic if the developer messed up the `else` statement here
assert(_isWrapperTraderType(_tradersPath[i].traderType));
Require.that(
i == _tradersPath.length - 1,
FILE,
"Wrapper must be the last trader"
);
Actions.ActionArgs[] memory wrapperActions = IIsolationModeWrapperTraderV2(_tradersPath[i].trader)
.createActionsForWrapping(
IIsolationModeWrapperTraderV2.CreateActionsForWrappingParams({
primaryAccountId: ZAP_ACCOUNT_ID,
otherAccountId: _otherAccountId(),
primaryAccountOwner: _accounts[ZAP_ACCOUNT_ID].owner,
primaryAccountNumber: _accounts[ZAP_ACCOUNT_ID].number,
otherAccountOwner: _accounts[_otherAccountId()].owner,
otherAccountNumber: _accounts[_otherAccountId()].number,
outputMarket: _marketIdsPath[i + 1],
inputMarket: _marketIdsPath[i],
minOutputAmount: _getMinOutputAmountWeiForIndex(_minOutputAmountWei, i, _tradersPath.length),
inputAmount: _getInputAmountWeiForIndex(_inputAmountWei, i),
orderData: _tradersPath[i].tradeData
})
);
for (uint256 j = 0; j < wrapperActions.length; j++) {
_actions[_cache.actionsCursor++] = wrapperActions[j];
}
}
}
// When the trades are finished, transfer all of the outputMarket from the ZAP account to the TRADE account
_actions[_cache.actionsCursor++] = AccountActionLib.encodeTransferAction(
ZAP_ACCOUNT_ID,
TRADE_ACCOUNT_ID,
_marketIdsPath[_marketIdsPath.length - 1],
AccountActionLib.all()
);
}
/**
* @return The index of the account that is not the Zap account. For the liquidation contract, this is
* the account being liquidated. For the GenericTrader contract this is the same as the trader account.
*/
function _otherAccountId() internal pure returns (uint256);
function _isWrapperTraderType(
TraderType _traderType
)
internal
pure
returns (bool)
{
return TraderType.IsolationModeWrapper == _traderType;
}
function _isUnwrapperTraderType(
TraderType _traderType
)
internal
pure
returns (bool)
{
return TraderType.IsolationModeUnwrapper == _traderType;
}
// ==================== Private Functions ====================
function _calculateZapAccountNumber(
address _tradeAccountOwner,
uint256 _tradeAccountNumber
)
private
view
returns (uint256)
{
return uint256(keccak256(abi.encodePacked(_tradeAccountOwner, _tradeAccountNumber, block.timestamp)));
}
function _getInputAmountWeiForIndex(
uint256 _inputAmountWei,
uint256 _index
)
private
pure
returns (uint256)
{
return _index == 0 ? _inputAmountWei : AccountActionLib.all();
}
function _getMinOutputAmountWeiForIndex(
uint256 _minOutputAmountWei,
uint256 _index,
uint256 _tradersPathLength
)
private
pure
returns (uint256)
{
return _index == _tradersPathLength - 1 ? _minOutputAmountWei : 1;
}
function _hashSubstring(
string memory _value,
uint256 _startIndex,
uint256 _endIndex
)
private
pure
returns (bytes32)
{
bytes memory strBytes = bytes(_value);
bytes memory result = new bytes(_endIndex - _startIndex);
for (uint256 i = _startIndex; i < _endIndex; i++) {
result[i - _startIndex] = strBytes[i];
}
return keccak256(result);
}
}{
"remappings": [],
"optimizer": {
"enabled": true,
"runs": 10000
},
"evmVersion": "istanbul",
"libraries": {},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"_expiryProxy","type":"address"},{"internalType":"address","name":"_dolomiteMargin","type":"address"},{"internalType":"address","name":"_liquidatorAssetRegistry","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"constant":true,"inputs":[],"name":"DOLOMITE_MARGIN","outputs":[{"internalType":"contract IDolomiteMargin","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"EXPIRY","outputs":[{"internalType":"contract IExpiry","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"LIQUIDATOR_ASSET_REGISTRY","outputs":[{"internalType":"contract ILiquidatorAssetRegistry","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"contract IDolomiteMargin","name":"_dolomiteMargin","type":"address"},{"internalType":"uint256","name":"_marketId","type":"uint256"}],"name":"isIsolationModeMarket","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"components":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"number","type":"uint256"}],"internalType":"struct Account.Info","name":"_solidAccount","type":"tuple"},{"components":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"number","type":"uint256"}],"internalType":"struct Account.Info","name":"_liquidAccount","type":"tuple"},{"internalType":"uint256[]","name":"_marketIdsPath","type":"uint256[]"},{"internalType":"uint256","name":"_inputAmountWei","type":"uint256"},{"internalType":"uint256","name":"_minOutputAmountWei","type":"uint256"},{"components":[{"internalType":"enum IGenericTraderProxyBase.TraderType","name":"traderType","type":"uint8"},{"internalType":"uint256","name":"makerAccountIndex","type":"uint256"},{"internalType":"address","name":"trader","type":"address"},{"internalType":"bytes","name":"tradeData","type":"bytes"}],"internalType":"struct IGenericTraderProxyBase.TraderParam[]","name":"_tradersPath","type":"tuple[]"},{"components":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"number","type":"uint256"}],"internalType":"struct Account.Info[]","name":"_makerAccounts","type":"tuple[]"},{"internalType":"uint256","name":"_expiry","type":"uint256"}],"name":"liquidate","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
60806040523480156200001157600080fd5b5060405162005b3438038062005b34833981016040819052620000349162000095565b60008054600160a01b6001600160a01b03199182166001600160a01b039485161760ff60a01b1916179091556001805482169483169490941790935560028054909316911617905562000115565b80516200008f81620000fb565b92915050565b600080600060608486031215620000ab57600080fd5b6000620000b9868662000082565b9350506020620000cc8682870162000082565b9250506040620000df8682870162000082565b9150509250925092565b60006001600160a01b0382166200008f565b6200010681620000e9565b81146200011257600080fd5b50565b615a0f80620001256000396000f3fe608060405234801561001057600080fd5b50600436106100675760003560e01c806379ebe04f1161005057806379ebe04f146100925780638211e8dc146100a7578063cf530f88146100c757610067565b806315c14a4a1461006c5780634530a2d71461008a575b600080fd5b6100746100cf565b6040516100819190615664565b60405180910390f35b6100746100de565b6100a56100a0366004614dc3565b6100ed565b005b6100ba6100b5366004614d18565b6105dc565b6040516100819190615645565b61007461078b565b6002546001600160a01b031681565b6001546001600160a01b031681565b60005474010000000000000000000000000000000000000000900460ff1661014a576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610141906156a2565b60405180910390fd5b600080547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff16905561017a6143f3565b60408051610120810182526002546001600160a01b03168152600060208201819052918101829052606081018290526003608082015260a081019190915260c081016101c461079a565b81526020016101d161079a565b81526020016101de61079a565b905290506101eb876107ba565b6101f5868661080d565b610201818885876108ad565b61021f8460008151811061021157fe5b60200260200101518761093a565b610227614464565b81516001600160a01b03168152602081018a9052604081018990528751889060009061024f57fe5b60200260200101518160600181815250508760018951038151811061027057fe5b602002602001015181608001818152505061028b81846109aa565b6102988160600151610c5d565b6102a58160800151610c5d565b805160408083015190517f0f47fab00000000000000000000000000000000000000000000000000000000081526001600160a01b0390921691630f47fab0916102f0916004016156c3565b60006040518083038186803b15801561030857600080fd5b505afa15801561031c573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526103449190810190614cc5565b60c082015280516040517f0f47fab00000000000000000000000000000000000000000000000000000000081526103f391906001600160a01b03821690630f47fab090610395908f906004016156c3565b60006040518083038186803b1580156103ad57600080fd5b505afa1580156103c1573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526103e99190810190614cc5565b8360c00151610d42565b60a082015282610404576000610411565b6001546001600160a01b03165b6001600160a01b031660e082015263ffffffff83166101008201526104346144d4565b61043d82610fca565b905061044882611398565b610451816115a9565b61045b8782611664565b9650606061047384878e600001518f602001516116b0565b90508a8160028151811061048357fe5b60200260200101819052506104ad848260018151811061049f57fe5b60200260200101518c611782565b60606104b88861184d565b6001016040519080825280602002602001820160405280156104f457816020015b6104e1614541565b8152602001906001900390816104d95790505b50905061050381858588611959565b6105128282878e8e8e8e611a0a565b84516040517fa67a6a450000000000000000000000000000000000000000000000000000000081526001600160a01b039091169063a67a6a459061055c9085908590600401615620565b600060405180830381600087803b15801561057657600080fd5b505af115801561058a573d6000803e3d6000fd5b5050600080547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff1674010000000000000000000000000000000000000000179055505050505050505050505050505050565b6000806060610696856001600160a01b031663062bd3e9866040518263ffffffff1660e01b815260040161061091906156ec565b60206040518083038186803b15801561062857600080fd5b505afa15801561063c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506106609190810190614c72565b6040805160208101909152600081527f06fdde03000000000000000000000000000000000000000000000000000000009061228f565b91509150816106aa57600092505050610785565b6060818060200190516106c09190810190614d52565b9050601381511015801561071b57506040518060400160405280601381526020017f446f6c6f6d6974652049736f6c6174696f6e3a00000000000000000000000000815250805190602001206107198260006013612367565b145b8061077f575060408051808201909152601a81527f446f6c6f6d6974653a20466565202b205374616b656420474c500000000000006020918201528151908201207ff423f1c88d15b6c0b5eba4c4269b960e9de5eca85f47ec00f20e4c0393f6a940145b93505050505b92915050565b6000546001600160a01b031681565b6107a2614593565b50604080518082019091526000808252602082015290565b61080a6002825110157f47656e6572696354726164657250726f787942617365000000000000000000007f496e76616c6964206d61726b65742070617468206c656e677468000000000000612418565b50565b61085b600083117f47656e6572696354726164657250726f787942617365000000000000000000007f496e76616c696420696e707574416d6f756e7457656900000000000000000000612418565b6108a9600082117f47656e6572696354726164657250726f787942617365000000000000000000007f496e76616c6964206d696e4f7574707574416d6f756e74576569000000000000612418565b5050565b6108ff81516001018451147f47656e6572696354726164657250726f787942617365000000000000000000007f496e76616c6964207472616465727320706172616d73206c656e677468000000612418565b60005b81518110156109335761092b85858585858151811061091d57fe5b6020026020010151856124a2565b600101610902565b5050505050565b81516109459061255e565b806109565750815161095690612576565b156108a9576108a960001982147f4c697175696461746f7250726f787956340000000000000000000000000000007f496e76616c696420616d6f756e7420666f722049736f6c6174696f6e4d6f6465612418565b81516001600160a01b03166109bb57fe5b6020820151516001600160a01b03166109d057fe5b6040820151516001600160a01b03166109e557fe5b610a408260600151836080015114157f4c697175696461746f7250726f787942617365000000000000000000000000007f4f776564206d61726b657420657175616c732068656c64206d61726b65740000856080015161258b565b610b21610ad483600001516001600160a01b03166347d1b53c856040015186608001516040518363ffffffff1660e01b8152600401610a809291906156d1565b604080518083038186803b158015610a9757600080fd5b505afa158015610aab573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610acf9190810190614ebd565b612623565b157f4c697175696461746f7250726f787942617365000000000000000000000000007f4f776564206d61726b65742063616e6e6f7420626520706f7369746976650000856080015161258b565b610bad610b6183600001516001600160a01b03166347d1b53c856040015186606001516040518363ffffffff1660e01b8152600401610a809291906156d1565b7f4c697175696461746f7250726f787942617365000000000000000000000000007f48656c64206d61726b65742063616e6e6f74206265206e656761746976650000856060015161258b565b610c01818263ffffffff16147f4c697175696461746f7250726f787942617365000000000000000000000000007f457870697279206f766572666c6f7773000000000000000000000000000000008461258b565b6108a9610c0c61264b565b63ffffffff168211157f4c697175696461746f7250726f787942617365000000000000000000000000007f426f72726f77206e6f74207965742065787069726564000000000000000000008461258b565b6000546040517fd9f4760100000000000000000000000000000000000000000000000000000000815261080a916001600160a01b03169063d9f4760190610caa90859030906004016156fa565b60206040518083038186803b158015610cc257600080fd5b505afa158015610cd6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610cfa9190810190614cfa565b7f4861734c697175696461746f72526567697374727900000000000000000000007f4173736574206e6f742077686974656c697374656400000000000000000000008461258b565b606080610dbe856001600160a01b031663295c39a56040518163ffffffff1660e01b815260040160206040518083038186803b158015610d8157600080fd5b505afa158015610d95573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610db99190810190614f29565b61265b565b90506000610dcd858383612694565b9050610dda848383612694565b90506000809050606082604051908082528060200260200182016040528015610e1d57816020015b610e0a6145aa565b815260200190600190039081610e025790505b50905060005b8451811015610fbc576000858281518110610e3a57fe5b602002602001015190505b8015610fa5576000610e56826126f7565b90506000610e648483612892565b905060405180606001604052808281526020018d6001600160a01b0316638928378e846040518263ffffffff1660e01b8152600401610ea391906156ec565b60206040518083038186803b158015610ebb57600080fd5b505afa158015610ecf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610ef39190810190614d87565b81526020018d6001600160a01b03166356ea84b2846040518263ffffffff1660e01b8152600401610f2491906156ec565b60606040518083038186803b158015610f3c57600080fd5b505afa158015610f50573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610f749190810190614da5565b90528551600188019787918110610f8757fe5b6020026020010181905250610f9c838361289d565b92505050610e45565b84841415610fb35750610fbc565b50600101610e23565b5093505050505b9392505050565b610fd26144d4565b610fda6145aa565b610fec8360a0015184606001516128a6565b9050610ff66145aa565b6110088460a0015185608001516128a6565b905060008085610100015163ffffffff1611156110c2576110276145d1565b8560e001516001600160a01b031663b1396542876060015188608001518961010001516040518463ffffffff1660e01b815260040161106893929190615750565b604080518083038186803b15801561107f57600080fd5b505afa158015611093573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506110b79190810190614edb565b519250611199915050565b6110ca6145d1565b8551606087015160808801516040517fd24c48bc0000000000000000000000000000000000000000000000000000000081526001600160a01b039093169263d24c48bc9261111c929091600401615735565b60206040518083038186803b15801561113457600080fd5b505afa158015611148573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061116c9190810190614d87565b9050611195611183846020015160000151836128bc565b6020850151519063ffffffff6128d516565b9150505b604051806101400160405280600081526020016000815260200161124987600001516001600160a01b03166347d1b53c89602001518a606001516040518363ffffffff1660e01b81526004016111f09291906156d1565b604080518083038186803b15801561120757600080fd5b505afa15801561121b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061123f9190810190614ebd565b8660400151612914565b81526020016112e487600001516001600160a01b03166347d1b53c89602001518a608001516040518363ffffffff1660e01b815260040161128b9291906156d1565b604080518083038186803b1580156112a257600080fd5b505afa1580156112b6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506112da9190810190614ebd565b8560400151612914565b815260200161132687600001516001600160a01b03166347d1b53c89604001518a606001516040518363ffffffff1660e01b81526004016111f09291906156d1565b815260200161136887600001516001600160a01b03166347d1b53c89604001518a608001516040518363ffffffff1660e01b815260040161128b9291906156d1565b8152600060208083019190915294850151516040820152929093015151606083015260809091015290505b919050565b602081015151611499906001600160a01b0316331480611451575081516020830151516040517f3a031bf00000000000000000000000000000000000000000000000000000000081526001600160a01b0390921691633a031bf091611401913390600401615605565b60206040518083038186803b15801561141957600080fd5b505afa15801561142d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506114519190810190614cfa565b7f4c697175696461746f7250726f787942617365000000000000000000000000007f53656e646572206e6f74206f70657261746f7200000000000000000000000000336129c5565b61010081015163ffffffff161561080a5760008160e001516001600160a01b0316631be7dd83836040015184608001516040518363ffffffff1660e01b81526004016114e69291906156d1565b60206040518083038186803b1580156114fe57600080fd5b505afa158015611512573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506115369190810190614f8f565b90506108a982610100015163ffffffff168263ffffffff16147f4c697175696461746f7250726f787942617365000000000000000000000000007f457870697279206d69736d6174636800000000000000000000000000000000008463ffffffff1686610100015163ffffffff16612a27565b60006115ca8260800151602001518360e00151612aeb90919063ffffffff16565b905060006115ee8360a0015160200151846101200151612aeb90919063ffffffff16565b90508082101561163057608083015160209081018051918501919091525160e0840151610120850151611622929190612b3f565b8352600160c084015261165f565b61164c8360a00151602001518461012001518560e00151612ba4565b60208085019190915260a0840151015183525b505050565b805160009061166f57fe5b508160001981141561168357508051610785565b81518110156107855780825261012082015160e08301516116a5918391612ba4565b602083015292915050565b60608084518660800151016040519080825280602002602001820160405280156116f457816020015b6116e1614593565b8152602001906001900390816116d95790505b5090506040518060400160405280856001600160a01b03168152602001848152508160008151811061172257fe5b60200260200101819052506040518060400160405280856001600160a01b031681526020016117518686612bba565b8152508160018151811061176157fe5b6020026020010181905250611777868683612bf0565b90505b949350505050565b60005b81518110156118475783600001516001600160a01b03166347d1b53c848484815181106117ae57fe5b60200260200101516040518363ffffffff1660e01b81526004016117d39291906156d1565b604080518083038186803b1580156117ea57600080fd5b505afa1580156117fe573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506118229190810190614ebd565b602001516fffffffffffffffffffffffffffffffff161561183f57fe5b600101611785565b50505050565b60006002815b83518110156119525761187c84828151811061186b57fe5b60200260200101516000015161255e565b156119125783818151811061188d57fe5b6020026020010151604001516001600160a01b031663b189111a6040518163ffffffff1660e01b815260040160206040518083038186803b1580156118d157600080fd5b505afa1580156118e5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506119099190810190614f29565b8201915061194a565b61193284828151811061192157fe5b602002602001015160000151612576565b156119435783818151811061188d57fe5b6001820191505b600101611853565b5092915050565b61010083015163ffffffff16156119c45761199a60006002856080015186606001518760e00151886101000151886020015189600001518a60c00151612cc0565b848260a0018051809190600101815250815181106119b457fe5b6020026020010181905250611847565b6119df60006002856080015186606001518660000151612dbd565b848260a0018051809190600101815250815181106119f957fe5b602002602001018190525050505050565b600019831415611b0857611ade6000600186600081518110611a2857fe5b602002602001015188600001516001600160a01b031663c190c2ec8c600081518110611a5057fe5b60200260200101518a600081518110611a6557fe5b60200260200101516040518363ffffffff1660e01b8152600401611a8a9291906156d1565b604080518083038186803b158015611aa157600080fd5b505afa158015611ab5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611ad99190810190614f0b565b612e3c565b868660a001805180919060010181525081518110611af857fe5b6020026020010181905250611b50565b611b2a6000600186600081518110611b1c57fe5b602002602001015186612ece565b868660a001805180919060010181525081518110611b4457fe5b60200260200101819052505b60005b8151811015612234576000828281518110611b6a57fe5b6020026020010151600001516003811115611b8157fe5b1415611c2c57611c026001868381518110611b9857fe5b6020026020010151878460010181518110611baf57fe5b6020026020010151858581518110611bc357fe5b602002602001015160400151611bd98987612f9d565b611be589888a51612fb9565b888881518110611bf157fe5b602002602001015160600151612fd4565b878760a001805180919060010181525081518110611c1c57fe5b602002602001018190525061222c565b6001828281518110611c3a57fe5b6020026020010151600001516003811115611c5157fe5b1415611d865760006060838381518110611c6757fe5b602002602001015160600151806020019051611c869190810190614f47565b91509150611ced836000148015611c9c57508683145b80611ca657508315155b7f47656e6572696354726164657250726f787942617365000000000000000000007f496e76616c696420637573746f6d20696e70757420616d6f756e740000000000612418565b611d5a60018960800151868681518110611d0357fe5b60200260200101516020015101898681518110611d1c57fe5b60200260200101518a8760010181518110611d3357fe5b6020026020010151888881518110611d4757fe5b602002602001015160400151878761309f565b898960a001805180919060010181525081518110611d7457fe5b6020026020010181905250505061222c565b611d9582828151811061186b57fe5b15611fb6578015611da257fe5b6060828281518110611db057fe5b6020026020010151604001516001600160a01b031663f93cffde60405180610160016040528060018152602001611de561311d565b81526020018c600181518110611df757fe5b6020026020010151600001516001600160a01b031681526020018c600181518110611e1e57fe5b60200260200101516020015181526020018c611e3861311d565b81518110611e4257fe5b6020026020010151600001516001600160a01b031681526020018c611e6561311d565b81518110611e6f57fe5b6020026020010151602001518152602001898660010181518110611e8f57fe5b60200260200101518152602001898681518110611ea857fe5b60200260200101518152602001611ec188878951612fb9565b8152602001611ed08987612f9d565b8152602001868681518110611ee157fe5b6020026020010151606001518152506040518263ffffffff1660e01b8152600401611f0c91906156b2565b60006040518083038186803b158015611f2457600080fd5b505afa158015611f38573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611f609190810190614c90565b905060005b8151811015611faf57818181518110611f7a57fe5b6020026020010151898960a001805180919060010181525081518110611f9c57fe5b6020908102919091010152600101611f65565b505061222c565b611fc582828151811061192157fe5b611fcb57fe5b61201c600183510382147f47656e6572696354726164657250726f787942617365000000000000000000007f57726170706572206d75737420626520746865206c6173742074726164657200612418565b606082828151811061202a57fe5b6020026020010151604001516001600160a01b0316634e2e55806040518061016001604052806001815260200161205f61311d565b81526020018c60018151811061207157fe5b6020026020010151600001516001600160a01b031681526020018c60018151811061209857fe5b60200260200101516020015181526020018c6120b261311d565b815181106120bc57fe5b6020026020010151600001516001600160a01b031681526020018c6120df61311d565b815181106120e957fe5b602002602001015160200151815260200189866001018151811061210957fe5b6020026020010151815260200189868151811061212257fe5b6020026020010151815260200161213b88878951612fb9565b815260200161214a8987612f9d565b815260200186868151811061215b57fe5b6020026020010151606001518152506040518263ffffffff1660e01b815260040161218691906156b2565b60006040518083038186803b15801561219e57600080fd5b505afa1580156121b2573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526121da9190810190614c90565b905060005b8151811015612229578181815181106121f457fe5b6020026020010151898960a00180518091906001018152508151811061221657fe5b60209081029190910101526001016121df565b50505b600101611b53565b50612261600160008660018851038151811061224c57fe5b602002602001015161225c613122565b612ece565b868660a00180518091906001018152508151811061227b57fe5b602002602001018190525050505050505050565b60006060846001600160a01b031684846040516024016122af9190615653565b60408051601f198184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090941693909317909252905161231a91906154e9565b600060405180830381855afa9150503d8060008114612355576040519150601f19603f3d011682016040523d82523d6000602084013e61235a565b606091505b5090969095509350505050565b6000606084905060608484036040519080825280601f01601f19166020018201604052801561239d576020820181803883390190505b509050845b84811015612406578281815181106123b657fe5b602001015160f81c60f81b82878303815181106123cf57fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053506001016123a2565b50805160209091012095945050505050565b8261165f5761242682613128565b7f3a2000000000000000000000000000000000000000000000000000000000000061245083613128565b604051602001612462939291906154f5565b60408051601f19818403018152908290527f08c379a000000000000000000000000000000000000000000000000000000000825261014191600401615653565b60408201516124fe906001600160a01b031615157f47656e6572696354726164657250726f787942617365000000000000000000007f496e76616c69642074726164657220617420696e6465780000000000000000008461258b565b600084828151811061250c57fe5b60200260200101519050600085836001018151811061252757fe5b6020026020010151905061253d878383876131c4565b61254a87838387876134b7565b612555858585613c2e565b50505050505050565b600081600381111561256c57fe5b60025b1492915050565b600081600381111561258457fe5b600361256f565b836118475761259983613128565b7f3a200000000000000000000000000000000000000000000000000000000000006125c384613128565b7f203c0000000000000000000000000000000000000000000000000000000000006125ed85613d28565b6040516124629594939291907f3e000000000000000000000000000000000000000000000000000000000000009060200161551d565b80516000908015610785575050602001516fffffffffffffffffffffffffffffffff16151590565b600061265642613e19565b905090565b60606001610100830401604051908082528060200260200182016040528015611952578160200160208202803883390190505092915050565b6000805b84518110156126ee576126be848683815181106126b157fe5b6020026020010151613e70565b6126e6576126df848683815181106126d257fe5b6020026020010151613eb0565b6001830192505b600101612698565b50909392505050565b600060ff6fffffffffffffffffffffffffffffffff83161561273a577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8001612742565b608083901c92505b67ffffffffffffffff831615612779577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc001612781565b604083901c92505b63ffffffff83161561279657601f190161279e565b602083901c92505b61ffff8316156127cf577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016127d7565b601083901c92505b60ff831615612807577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80161280f565b600883901c92505b600f83161561283f577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc01612847565b600483901c92505b6003831615612877577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0161287f565b600283901c92505b6001831615610785576000190192915050565b610100919091020190565b6001901b900390565b6128ae6145aa565b610fc3836000855185613ee6565b6000610fc3838360000151670de0b6b3a7640000612ba4565b600082820183811015610fc3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161014190615672565b61291c614593565b602083015183516fffffffffffffffffffffffffffffffff90911690156129885760408051808201909152600181526020848101519082019061297d9084906bffffffffffffffffffffffff16670de0b6b3a764000063ffffffff613fd016565b815250915050610785565b60408051808201909152600081528351602082019061297d9084906bffffffffffffffffffffffff16670de0b6b3a764000063ffffffff613fd016565b83611847576129d383613128565b7f3a200000000000000000000000000000000000000000000000000000000000006129fd84613128565b7f203c0000000000000000000000000000000000000000000000000000000000006125ed8561405a565b8461093357612a3584613128565b7f3a20000000000000000000000000000000000000000000000000000000000000612a5f85613128565b7f203c000000000000000000000000000000000000000000000000000000000000612a8986613d28565b7f2c20000000000000000000000000000000000000000000000000000000000000612ab387613d28565b60405161246297969594939291907f3e000000000000000000000000000000000000000000000000000000000000009060200161557b565b600082612afa57506000610785565b82820282848281612b0757fe5b0414610fc3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161014190615692565b6000831580612b4c575082155b15612b6357612b5c6000836141c4565b9050610fc3565b61177a6001612b9884612b8c83612b808a8a63ffffffff612aeb16565b9063ffffffff61420616565b9063ffffffff6141c416565b9063ffffffff6128d516565b600061177a82612b8c868663ffffffff612aeb16565b6000828242604051602001612bd19392919061549d565b60408051601f1981840301815291905280516020909101209392505050565b60005b825181101561184757612c04614593565b828286608001510181518110612c1657fe5b6020026020010151905060006001600160a01b031681600001516001600160a01b0316148015612c4857506020810151155b612c4e57fe5b6040518060400160405280858481518110612c6557fe5b6020026020010151600001516001600160a01b03168152602001858481518110612c8b57fe5b602002602001015160200151815250838387608001510181518110612cac57fe5b602090810291909101015250600101612bf3565b612cc8614541565b612cd06145e4565b82612d00576040805160808101909152600181526020810160008152602001600081526020018590529050612d2e565b82612d0757fe5b60408051608081019091526000808252602082019081526020016000815260200186905290505b60408051610100810190915280600581526020018c81526020018281526020018415612d5a5789612d5c565b8a5b81526020018415612d6d578a612d6f565b895b8152602001886001600160a01b031681526020018b81526020018a88604051602001612d9c929190615778565b60408051601f1981840301815291905290529b9a5050505050505050505050565b612dc5614541565b50604080516101008101825260068152602080820197909752815160808082018452600182526000828a0181905282850181905260608084019690965283850192909252938201959095529182019290925260a0810183905260c0810193909352805191825292810190925260e081019190915290565b612e44614541565b60408051610100810190915280600281526020808201889052604080516080810182528651151581529201919081016000815260200160018152602001856020015181525081526020018481526020016000815260200160006001600160a01b03168152602001858152602001604051806020016040528060008152508152509050949350505050565b612ed6614541565b612ede6145e4565b600019831415612f155760408051608081019091526001815260208101600081526020016001815260200160008152509050612f3c565b60408051608081019091526000808252602082019081526020016000815260200184905290505b60408051610100810190915280600281526020018781526020018281526020018581526020016000815260200160006001600160a01b0316815260200186815260200160405180602001604052806000815250815250915050949350505050565b60008115612fb257612fad613122565b610fc3565b5090919050565b6000600182038314612fcc57600161177a565b509192915050565b612fdc614541565b60408051610100810182526004815260208082018b905282516080810184526000808252929384019290918201908152602001600019881461301f576000613022565b60015b600181111561302d57fe5b815260200160001988146130415787613044565b60005b8152508152602001888152602001878152602001866001600160a01b03168152602001600081526020018484604051602001613081929190615715565b60408051601f19818403018152919052905298975050505050505050565b6130a7614541565b604080516101008101909152806005815260208082018b905260408051608081018252600181529201919081016000815260200160008152602001868152508152602001878152602001868152602001856001600160a01b03168152602001888152602001838152509050979650505050505050565b600290565b60001990565b6060808260405160200161313c91906154d4565b60408051601f19818403018152919052905060205b80156131a95781516000199091019082908290811061316c57fe5b01602001517fff0000000000000000000000000000000000000000000000000000000000000016156131a45760010181529050611393565b613151565b5060408051600080825260208201909252905b509392505050565b83516131d090846105dc565b156133bf576132406131e5826000015161255e565b7f47656e6572696354726164657250726f787942617365000000000000000000007f496e76616c69642069736f6c6174696f6e206d6f646520756e77726170706572868560000151600381111561323857fe5b60ff16612a27565b835161324c90836105dc565b156133ba5783516040517f062bd3e90000000000000000000000000000000000000000000000000000000081526000916001600160a01b03169063062bd3e99061329a9086906004016156ec565b60206040518083038186803b1580156132b257600080fd5b505afa1580156132c6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506132ea9190810190614c72565b90506133b8816001600160a01b03166316774ba084604001516040518263ffffffff1660e01b815260040161331f91906155f7565b60206040518083038186803b15801561333757600080fd5b505afa15801561334b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061336f9190810190614cfa565b7f47656e6572696354726164657250726f787942617365000000000000000000007f496e76616c696420756e777261702073657175656e63650000000000000000008787612a27565b505b611847565b83516133cb90836105dc565b15613433576133ba6133e08260000151612576565b7f47656e6572696354726164657250726f787942617365000000000000000000007f496e76616c69642069736f6c6174696f6e206d6f646520777261707065720000858560000151600381111561323857fe5b61184760008251600381111561344557fe5b148061345d575060018251600381111561345b57fe5b145b7f47656e6572696354726164657250726f787942617365000000000000000000007f496e76616c696420747261646572207479706500000000000000000000000000846000015160038111156134af57fe5b60ff1661258b565b81516134c29061255e565b1561387657604080830151865191517f062bd3e900000000000000000000000000000000000000000000000000000000815290916000916001600160a01b039091169063062bd3e9906135199089906004016156ec565b60206040518083038186803b15801561353157600080fd5b505afa158015613545573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506135699190810190614c72565b905061363c816001600160a01b0316836001600160a01b031663fc0c546a6040518163ffffffff1660e01b815260040160206040518083038186803b1580156135b157600080fd5b505afa1580156135c5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506135e99190810190614c72565b6001600160a01b0316147f47656e6572696354726164657250726f787942617365000000000000000000007f496e76616c696420696e70757420666f7220756e777261707065720000000000868a612a27565b613786826001600160a01b031663f1a1f8fa89600001516001600160a01b031663062bd3e9896040518263ffffffff1660e01b815260040161367e91906156ec565b60206040518083038186803b15801561369657600080fd5b505afa1580156136aa573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506136ce9190810190614c72565b6040518263ffffffff1660e01b81526004016136ea91906155f7565b60206040518083038186803b15801561370257600080fd5b505afa158015613716573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061373a9190810190614cfa565b7f47656e6572696354726164657250726f787942617365000000000000000000007f496e76616c6964206f757470757420666f7220756e77726170706572000000008660010189612a27565b60408085015190517f16774ba000000000000000000000000000000000000000000000000000000000815261386f916001600160a01b038416916316774ba0916137d2916004016155f7565b60206040518083038186803b1580156137ea57600080fd5b505afa1580156137fe573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506138229190810190614cfa565b7f47656e6572696354726164657250726f787942617365000000000000000000007f556e7772617070657220747261646572206e6f7420656e61626c65640000000087604001518a614248565b5050610933565b815161388190612576565b1561093357604080830151865191517f062bd3e900000000000000000000000000000000000000000000000000000000815290916000916001600160a01b039091169063062bd3e9906138d89088906004016156ec565b60206040518083038186803b1580156138f057600080fd5b505afa158015613904573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506139289190810190614c72565b9050613a71826001600160a01b0316639f4a129789600001516001600160a01b031663062bd3e98a6040518263ffffffff1660e01b815260040161396c91906156ec565b60206040518083038186803b15801561398457600080fd5b505afa158015613998573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506139bc9190810190614c72565b6040518263ffffffff1660e01b81526004016139d891906155f7565b60206040518083038186803b1580156139f057600080fd5b505afa158015613a04573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250613a289190810190614cfa565b7f47656e6572696354726164657250726f787942617365000000000000000000007f496e76616c696420696e70757420666f72207772617070657200000000000000868a612a27565b613b45816001600160a01b0316836001600160a01b031663fc0c546a6040518163ffffffff1660e01b815260040160206040518083038186803b158015613ab757600080fd5b505afa158015613acb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250613aef9190810190614c72565b6001600160a01b0316147f47656e6572696354726164657250726f787942617365000000000000000000007f496e76616c6964206f757470757420666f7220777261707065720000000000008660010189612a27565b60408085015190517f16774ba0000000000000000000000000000000000000000000000000000000008152612555916001600160a01b038416916316774ba091613b91916004016155f7565b60206040518083038186803b158015613ba957600080fd5b505afa158015613bbd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250613be19190810190614cfa565b7f47656e6572696354726164657250726f787942617365000000000000000000007f5772617070657220747261646572206e6f7420656e61626c6564000000000000876040015189614248565b81516003811115613c3b57fe5b60011415613cd557613cd083518360200151108015613c88575060006001600160a01b031684846020015181518110613c7057fe5b6020026020010151600001516001600160a01b031614155b7f47656e6572696354726164657250726f787942617365000000000000000000007f496e76616c6964206d616b6572206163636f756e74206f776e657200000000008461258b565b61165f565b61165f82602001516000147f47656e6572696354726164657250726f787942617365000000000000000000007f496e76616c6964206d616b6572206163636f756e74206f776e657200000000008461258b565b606081613d69575060408051808201909152600181527f30000000000000000000000000000000000000000000000000000000000000006020820152611393565b8160005b8115613d8157600101600a82049150613d6d565b6060816040519080825280601f01601f191660200182016040528015613dae576020820181803883390190505b508593509050815b8015613e105760001901600a840660300160f81b828281518110613dd657fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350600a84049350613db6565b50949350505050565b60008161078563ffffffff821682147f4d617468000000000000000000000000000000000000000000000000000000007f556e73616665206361737420746f2075696e74333200000000000000000000008461258b565b600080610100830490506000610100840690506000816001901b868481518110613e9657fe5b602002602001015116905060008111935050505092915050565b815161010082049060ff8316906001821b90859084908110613ece57fe5b60200260200101818151179150818152505050505050565b613eee6145aa565b838303801580613f205750806001148015613f20575082868681518110613f1157fe5b60200260200101516000015114155b15613f57576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161014190615682565b600060028204860190506000878281518110613f6f57fe5b602002602001015160000151905080851015613f9b57613f9188888488613ee6565b935050505061177a565b80851115613fb257613f9188836001018888613ee6565b878281518110613fbe57fe5b6020026020010151935050505061177a565b6000831580613fdd575082155b15613fed57612b5c6000836141c4565b6000613fff858563ffffffff612aeb16565b905061405161401e6001612b986002612b8c888463ffffffff61420616565b61402e838663ffffffff6142aa16565b101561403b57600061403e565b60015b60ff16612b98838663ffffffff6141c416565b95945050505050565b60408051602a80825260608281019093526001600160a01b038416918391602082018180388339019050509050603060f81b8160008151811061409957fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350607860f81b816001815181106140da57fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535060005b60148110156131bc5760028102614125600f85166142ec565b83826029038151811061413457fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350600484901c9350614176600f85166142ec565b83826028038151811061418557fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053505060049290921c9160010161410c565b6000610fc383836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f00000000000081525061430e565b6000610fc383836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f77000081525061435f565b846109335761425684613128565b7f3a2000000000000000000000000000000000000000000000000000000000000061428085613128565b7f203c000000000000000000000000000000000000000000000000000000000000612a898661405a565b6000610fc383836040518060400160405280601881526020017f536166654d6174683a206d6f64756c6f206279207a65726f00000000000000008152506143a5565b6000600a82101561430457506030810160f81b611393565b5060570160f81b90565b60008183614349576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016101419190615653565b50600083858161435557fe5b0495945050505050565b6000818484111561439d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016101419190615653565b505050900390565b600081836143e0576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016101419190615653565b508284816143ea57fe5b06949350505050565b60405180610120016040528060006001600160a01b0316815260200160006001600160a01b03168152602001600015158152602001600081526020016000815260200160008152602001614445614593565b8152602001614452614593565b815260200161445f614593565b905290565b60405180610120016040528060006001600160a01b03168152602001614488614593565b8152602001614495614593565b81526020016000815260200160008152602001606081526020016060815260200160006001600160a01b03168152602001600063ffffffff1681525090565b60405180610140016040528060008152602001600081526020016144f6614593565b8152602001614503614593565b8152602001614510614593565b815260200161451d614593565b81526020016000151581526020016000815260200160008152602001600081525090565b6040805161010081018252600080825260208201529081016145616145e4565b8152602001600081526020016000815260200160006001600160a01b0316815260200160008152602001606081525090565b604080518082019091526000808252602082015290565b6040518060600160405280600081526020016145c46145d1565b815260200161445f61460d565b6040518060200160405280600081525090565b604080516080810190915260008082526020820190815260200160008152602001600081525090565b604080516060810182526000808252602082018190529181019190915290565b80356107858161595b565b80516107858161595b565b600082601f83011261465457600080fd5b8151614667614662826157ba565b615793565b81815260209384019390925082018360005b838110156146a5578151860161468f888261492a565b8452506020928301929190910190600101614679565b5050505092915050565b600082601f8301126146c057600080fd5b81356146ce614662826157ba565b915081818352602084019350602081019050838560408402820111156146f357600080fd5b60005b838110156146a557816147098882614b03565b845250602090920191604091909101906001016146f6565b600082601f83011261473257600080fd5b8135614740614662826157ba565b81815260209384019390925082018360005b838110156146a557813586016147688882614b85565b8452506020928301929190910190600101614752565b600082601f83011261478f57600080fd5b813561479d614662826157ba565b915081818352602084019350602081019050838560208402820111156147c257600080fd5b60005b838110156146a557816147d88882614c46565b84525060209283019291909101906001016147c5565b600082601f8301126147ff57600080fd5b815161480d614662826157ba565b9150818183526020840193506020810190508385602084028201111561483257600080fd5b60005b838110156146a557816148488882614c51565b8452506020928301929190910190600101614835565b80516107858161596f565b600082601f83011261487a57600080fd5b8135614888614662826157db565b915080825260208301602083018583830111156148a457600080fd5b6148af8382846158ee565b50505092915050565b600082601f8301126148c957600080fd5b81516148d7614662826157db565b915080825260208301602083018583830111156148f357600080fd5b6148af8382846158fa565b803561078581615978565b805161078581615981565b80516107858161598e565b80356107858161599b565b6000610160828403121561493d57600080fd5b614948610100615793565b905060006149568484614909565b825250602061496784848301614c51565b602083015250604061497b84828501614a06565b60408301525060c061498f84828501614c51565b60608301525060e06149a384828501614c51565b6080830152506101006149b884828501614638565b60a0830152506101206149cd84828501614c51565b60c08301525061014082015167ffffffffffffffff8111156149ee57600080fd5b6149fa848285016148b8565b60e08301525092915050565b600060808284031215614a1857600080fd5b614a226080615793565b90506000614a30848461485e565b8252506020614a4184848301614914565b6020830152506040614a5584828501614914565b6040830152506060614a6984828501614c51565b60608301525092915050565b600060208284031215614a8757600080fd5b614a916020615793565b90506000614a9f8484614c51565b82525092915050565b600060608284031215614aba57600080fd5b614ac46060615793565b90506000614ad28484614c67565b8252506020614ae384848301614c67565b6020830152506040614af784828501614c5c565b60408301525092915050565b600060408284031215614b1557600080fd5b614b1f6040615793565b90506000614b2d848461462d565b8252506020614b3e84848301614c46565b60208301525092915050565b600060408284031215614b5c57600080fd5b614b666040615793565b90506000614b74848461485e565b8252506020614b3e84848301614c3b565b600060808284031215614b9757600080fd5b614ba16080615793565b90506000614baf848461491f565b8252506020614bc084848301614c46565b6020830152506040614bd48482850161462d565b604083015250606082013567ffffffffffffffff811115614bf457600080fd5b614a6984828501614869565b600060408284031215614c1257600080fd5b614c1c6040615793565b90506000614c2a848461485e565b8252506020614b3e84848301614c51565b8051610785816159a8565b8035610785816159b1565b8051610785816159b1565b8051610785816159ba565b8051610785816159c3565b600060208284031215614c8457600080fd5b600061177a8484614638565b600060208284031215614ca257600080fd5b815167ffffffffffffffff811115614cb957600080fd5b61177a84828501614643565b600060208284031215614cd757600080fd5b815167ffffffffffffffff811115614cee57600080fd5b61177a848285016147ee565b600060208284031215614d0c57600080fd5b600061177a848461485e565b60008060408385031215614d2b57600080fd5b6000614d3785856148fe565b9250506020614d4885828601614c46565b9150509250929050565b600060208284031215614d6457600080fd5b815167ffffffffffffffff811115614d7b57600080fd5b61177a848285016148b8565b600060208284031215614d9957600080fd5b600061177a8484614a75565b600060608284031215614db757600080fd5b600061177a8484614aa8565b600080600080600080600080610140898b031215614de057600080fd5b6000614dec8b8b614b03565b9850506040614dfd8b828c01614b03565b975050608089013567ffffffffffffffff811115614e1a57600080fd5b614e268b828c0161477e565b96505060a0614e378b828c01614c46565b95505060c0614e488b828c01614c46565b94505060e089013567ffffffffffffffff811115614e6557600080fd5b614e718b828c01614721565b93505061010089013567ffffffffffffffff811115614e8f57600080fd5b614e9b8b828c016146af565b925050610120614ead8b828c01614c46565b9150509295985092959890939650565b600060408284031215614ecf57600080fd5b600061177a8484614b4a565b60008060408385031215614eee57600080fd5b6000614efa8585614a75565b9250506020614d4885828601614a75565b600060408284031215614f1d57600080fd5b600061177a8484614c00565b600060208284031215614f3b57600080fd5b600061177a8484614c51565b60008060408385031215614f5a57600080fd5b6000614f668585614c51565b925050602083015167ffffffffffffffff811115614f8357600080fd5b614d48858286016148b8565b600060208284031215614fa157600080fd5b600061177a8484614c5c565b6000610fc383836152a1565b6000614fc5838361546b565b505060400190565b614fd6816158cd565b82525050565b614fd681615816565b614fd6614ff182615816565b615926565b600061500182615809565b61500b818561580d565b93508360208202850161501d85615803565b8060005b85811015615057578484038952815161503a8582614fad565b945061504583615803565b60209a909a0199925050600101615021565b5091979650505050505050565b600061506f82615809565b615079818561580d565b935061508483615803565b8060005b838110156150b257815161509c8882614fb9565b97506150a783615803565b925050600101615088565b509495945050505050565b614fd681615821565b614fd66150d282615826565b615870565b614fd66150d28261584b565b614fd66150d282615870565b60006150fa82615809565b615104818561580d565b93506151148185602086016158fa565b61511d81615937565b9093019392505050565b600061513282615809565b61513c8185611393565b935061514c8185602086016158fa565b9290920192915050565b614fd681615873565b614fd6816158d8565b614fd6816158e3565b600061517e601b8361580d565b7f536166654d6174683a206164646974696f6e206f766572666c6f770000000000815260200192915050565b60006151b760258361580d565b7f4c697175696461746f7250726f7879426173653a204d61726b6574206e6f742081527f666f756e64000000000000000000000000000000000000000000000000000000602082015260400192915050565b600061521660218361580d565b7f536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f81527f7700000000000000000000000000000000000000000000000000000000000000602082015260400192915050565b6000615275601f8361580d565b7f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00815260200192915050565b80516000906101608401906152b6858261515f565b5060208301516152c9602086018261548b565b5060408301516152dc6040860182615343565b5060608301516152ef60c086018261548b565b50608083015161530260e086018261548b565b5060a0830151615316610100860182614fdc565b5060c083015161532a61012086018261548b565b5060e083015184820361014086015261405182826150ef565b8051608083019061535484826150bd565b5060208201516153676020850182615168565b50604082015161537a6040850182615168565b506060820151611847606085018261548b565b80516000906101608401906153a2858261548b565b5060208301516153b5602086018261548b565b5060408301516153c86040860182614fdc565b5060608301516153db606086018261548b565b5060808301516153ee6080860182614fdc565b5060a083015161540160a086018261548b565b5060c083015161541460c086018261548b565b5060e083015161542760e086018261548b565b5061010083015161543c61010086018261548b565b5061012083015161545161012086018261548b565b5061014083015184820361014086015261405182826150ef565b8051604083019061547c8482614fdc565b50602082015161184760208501825b614fd681615870565b614fd6816158b3565b60006154a98286614fe5565b6014820191506154b982856150e3565b6020820191506154c982846150e3565b506020019392505050565b60006154e082846150e3565b50602001919050565b6000610fc38284615127565b60006155018286615127565b915061550d82856150d7565b6002820191506140518284615127565b60006155298289615127565b915061553582886150d7565b6002820191506155458287615127565b915061555182866150d7565b6002820191506155618285615127565b915061556d82846150c6565b506001019695505050505050565b6000615587828b615127565b9150615593828a6150d7565b6002820191506155a38289615127565b91506155af82886150d7565b6002820191506155bf8287615127565b91506155cb82866150d7565b6002820191506155db8285615127565b91506155e782846150c6565b5060010198975050505050505050565b602081016107858284614fdc565b604081016156138285614fdc565b610fc36020830184614fcd565b604080825281016156318185615064565b9050818103602083015261177a8184614ff6565b6020810161078582846150bd565b60208082528101610fc381846150ef565b602081016107858284615156565b6020808252810161078581615171565b60208082528101610785816151aa565b6020808252810161078581615209565b6020808252810161078581615268565b60208082528101610fc3818461538d565b60408101610785828461546b565b606081016156df828561546b565b610fc3604083018461548b565b60208101610785828461548b565b60408101615708828561548b565b610fc36020830184614fdc565b60408101615723828561548b565b818103602083015261177a81846150ef565b60408101615743828561548b565b610fc3602083018461548b565b6060810161575e828661548b565b61576b602083018561548b565b61177a6040830184615494565b60408101615786828561548b565b610fc36020830184615494565b60405181810167ffffffffffffffff811182821017156157b257600080fd5b604052919050565b600067ffffffffffffffff8211156157d157600080fd5b5060209081020190565b600067ffffffffffffffff8211156157f257600080fd5b506020601f91909101601f19160190565b60200190565b5190565b90815260200190565b6000610785826158a7565b151590565b7fff000000000000000000000000000000000000000000000000000000000000001690565b7fffff0000000000000000000000000000000000000000000000000000000000001690565b90565b600061078582615816565b8061139381615947565b8061139381615951565b6fffffffffffffffffffffffffffffffff1690565b6001600160a01b031690565b63ffffffff1690565b6bffffffffffffffffffffffff1690565b600061078582615873565b60006107858261587e565b600061078582615888565b82818337506000910152565b60005b838110156159155781810151838201526020016158fd565b838111156118475750506000910152565b600061078582600061078582615941565b601f01601f191690565b60601b90565b6009811061080a57fe5b6002811061080a57fe5b61596481615816565b811461080a57600080fd5b61596481615821565b61596481615873565b6009811061080a57600080fd5b6002811061080a57600080fd5b6004811061080a57600080fd5b61596481615892565b61596481615870565b615964816158b3565b615964816158bc56fea365627a7a72315820a1f7f7c0adea7cfb6f73fc2c0fbce4e3dcf6446017c30ef0a13f998b1e39b1cd6c6578706572696d656e74616cf564736f6c63430005100040000000000000000000000000dec1ae3b570ac3c57871bbd7bfeacc807f973bea0000000000000000000000006bd780e7fdf01d77e4d475c821f1e7ae0540907200000000000000000000000010d98759762efac656bd4be7f2f5599208f44fac
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106100675760003560e01c806379ebe04f1161005057806379ebe04f146100925780638211e8dc146100a7578063cf530f88146100c757610067565b806315c14a4a1461006c5780634530a2d71461008a575b600080fd5b6100746100cf565b6040516100819190615664565b60405180910390f35b6100746100de565b6100a56100a0366004614dc3565b6100ed565b005b6100ba6100b5366004614d18565b6105dc565b6040516100819190615645565b61007461078b565b6002546001600160a01b031681565b6001546001600160a01b031681565b60005474010000000000000000000000000000000000000000900460ff1661014a576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610141906156a2565b60405180910390fd5b600080547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff16905561017a6143f3565b60408051610120810182526002546001600160a01b03168152600060208201819052918101829052606081018290526003608082015260a081019190915260c081016101c461079a565b81526020016101d161079a565b81526020016101de61079a565b905290506101eb876107ba565b6101f5868661080d565b610201818885876108ad565b61021f8460008151811061021157fe5b60200260200101518761093a565b610227614464565b81516001600160a01b03168152602081018a9052604081018990528751889060009061024f57fe5b60200260200101518160600181815250508760018951038151811061027057fe5b602002602001015181608001818152505061028b81846109aa565b6102988160600151610c5d565b6102a58160800151610c5d565b805160408083015190517f0f47fab00000000000000000000000000000000000000000000000000000000081526001600160a01b0390921691630f47fab0916102f0916004016156c3565b60006040518083038186803b15801561030857600080fd5b505afa15801561031c573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526103449190810190614cc5565b60c082015280516040517f0f47fab00000000000000000000000000000000000000000000000000000000081526103f391906001600160a01b03821690630f47fab090610395908f906004016156c3565b60006040518083038186803b1580156103ad57600080fd5b505afa1580156103c1573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526103e99190810190614cc5565b8360c00151610d42565b60a082015282610404576000610411565b6001546001600160a01b03165b6001600160a01b031660e082015263ffffffff83166101008201526104346144d4565b61043d82610fca565b905061044882611398565b610451816115a9565b61045b8782611664565b9650606061047384878e600001518f602001516116b0565b90508a8160028151811061048357fe5b60200260200101819052506104ad848260018151811061049f57fe5b60200260200101518c611782565b60606104b88861184d565b6001016040519080825280602002602001820160405280156104f457816020015b6104e1614541565b8152602001906001900390816104d95790505b50905061050381858588611959565b6105128282878e8e8e8e611a0a565b84516040517fa67a6a450000000000000000000000000000000000000000000000000000000081526001600160a01b039091169063a67a6a459061055c9085908590600401615620565b600060405180830381600087803b15801561057657600080fd5b505af115801561058a573d6000803e3d6000fd5b5050600080547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff1674010000000000000000000000000000000000000000179055505050505050505050505050505050565b6000806060610696856001600160a01b031663062bd3e9866040518263ffffffff1660e01b815260040161061091906156ec565b60206040518083038186803b15801561062857600080fd5b505afa15801561063c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506106609190810190614c72565b6040805160208101909152600081527f06fdde03000000000000000000000000000000000000000000000000000000009061228f565b91509150816106aa57600092505050610785565b6060818060200190516106c09190810190614d52565b9050601381511015801561071b57506040518060400160405280601381526020017f446f6c6f6d6974652049736f6c6174696f6e3a00000000000000000000000000815250805190602001206107198260006013612367565b145b8061077f575060408051808201909152601a81527f446f6c6f6d6974653a20466565202b205374616b656420474c500000000000006020918201528151908201207ff423f1c88d15b6c0b5eba4c4269b960e9de5eca85f47ec00f20e4c0393f6a940145b93505050505b92915050565b6000546001600160a01b031681565b6107a2614593565b50604080518082019091526000808252602082015290565b61080a6002825110157f47656e6572696354726164657250726f787942617365000000000000000000007f496e76616c6964206d61726b65742070617468206c656e677468000000000000612418565b50565b61085b600083117f47656e6572696354726164657250726f787942617365000000000000000000007f496e76616c696420696e707574416d6f756e7457656900000000000000000000612418565b6108a9600082117f47656e6572696354726164657250726f787942617365000000000000000000007f496e76616c6964206d696e4f7574707574416d6f756e74576569000000000000612418565b5050565b6108ff81516001018451147f47656e6572696354726164657250726f787942617365000000000000000000007f496e76616c6964207472616465727320706172616d73206c656e677468000000612418565b60005b81518110156109335761092b85858585858151811061091d57fe5b6020026020010151856124a2565b600101610902565b5050505050565b81516109459061255e565b806109565750815161095690612576565b156108a9576108a960001982147f4c697175696461746f7250726f787956340000000000000000000000000000007f496e76616c696420616d6f756e7420666f722049736f6c6174696f6e4d6f6465612418565b81516001600160a01b03166109bb57fe5b6020820151516001600160a01b03166109d057fe5b6040820151516001600160a01b03166109e557fe5b610a408260600151836080015114157f4c697175696461746f7250726f787942617365000000000000000000000000007f4f776564206d61726b657420657175616c732068656c64206d61726b65740000856080015161258b565b610b21610ad483600001516001600160a01b03166347d1b53c856040015186608001516040518363ffffffff1660e01b8152600401610a809291906156d1565b604080518083038186803b158015610a9757600080fd5b505afa158015610aab573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610acf9190810190614ebd565b612623565b157f4c697175696461746f7250726f787942617365000000000000000000000000007f4f776564206d61726b65742063616e6e6f7420626520706f7369746976650000856080015161258b565b610bad610b6183600001516001600160a01b03166347d1b53c856040015186606001516040518363ffffffff1660e01b8152600401610a809291906156d1565b7f4c697175696461746f7250726f787942617365000000000000000000000000007f48656c64206d61726b65742063616e6e6f74206265206e656761746976650000856060015161258b565b610c01818263ffffffff16147f4c697175696461746f7250726f787942617365000000000000000000000000007f457870697279206f766572666c6f7773000000000000000000000000000000008461258b565b6108a9610c0c61264b565b63ffffffff168211157f4c697175696461746f7250726f787942617365000000000000000000000000007f426f72726f77206e6f74207965742065787069726564000000000000000000008461258b565b6000546040517fd9f4760100000000000000000000000000000000000000000000000000000000815261080a916001600160a01b03169063d9f4760190610caa90859030906004016156fa565b60206040518083038186803b158015610cc257600080fd5b505afa158015610cd6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610cfa9190810190614cfa565b7f4861734c697175696461746f72526567697374727900000000000000000000007f4173736574206e6f742077686974656c697374656400000000000000000000008461258b565b606080610dbe856001600160a01b031663295c39a56040518163ffffffff1660e01b815260040160206040518083038186803b158015610d8157600080fd5b505afa158015610d95573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610db99190810190614f29565b61265b565b90506000610dcd858383612694565b9050610dda848383612694565b90506000809050606082604051908082528060200260200182016040528015610e1d57816020015b610e0a6145aa565b815260200190600190039081610e025790505b50905060005b8451811015610fbc576000858281518110610e3a57fe5b602002602001015190505b8015610fa5576000610e56826126f7565b90506000610e648483612892565b905060405180606001604052808281526020018d6001600160a01b0316638928378e846040518263ffffffff1660e01b8152600401610ea391906156ec565b60206040518083038186803b158015610ebb57600080fd5b505afa158015610ecf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610ef39190810190614d87565b81526020018d6001600160a01b03166356ea84b2846040518263ffffffff1660e01b8152600401610f2491906156ec565b60606040518083038186803b158015610f3c57600080fd5b505afa158015610f50573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610f749190810190614da5565b90528551600188019787918110610f8757fe5b6020026020010181905250610f9c838361289d565b92505050610e45565b84841415610fb35750610fbc565b50600101610e23565b5093505050505b9392505050565b610fd26144d4565b610fda6145aa565b610fec8360a0015184606001516128a6565b9050610ff66145aa565b6110088460a0015185608001516128a6565b905060008085610100015163ffffffff1611156110c2576110276145d1565b8560e001516001600160a01b031663b1396542876060015188608001518961010001516040518463ffffffff1660e01b815260040161106893929190615750565b604080518083038186803b15801561107f57600080fd5b505afa158015611093573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506110b79190810190614edb565b519250611199915050565b6110ca6145d1565b8551606087015160808801516040517fd24c48bc0000000000000000000000000000000000000000000000000000000081526001600160a01b039093169263d24c48bc9261111c929091600401615735565b60206040518083038186803b15801561113457600080fd5b505afa158015611148573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061116c9190810190614d87565b9050611195611183846020015160000151836128bc565b6020850151519063ffffffff6128d516565b9150505b604051806101400160405280600081526020016000815260200161124987600001516001600160a01b03166347d1b53c89602001518a606001516040518363ffffffff1660e01b81526004016111f09291906156d1565b604080518083038186803b15801561120757600080fd5b505afa15801561121b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061123f9190810190614ebd565b8660400151612914565b81526020016112e487600001516001600160a01b03166347d1b53c89602001518a608001516040518363ffffffff1660e01b815260040161128b9291906156d1565b604080518083038186803b1580156112a257600080fd5b505afa1580156112b6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506112da9190810190614ebd565b8560400151612914565b815260200161132687600001516001600160a01b03166347d1b53c89604001518a606001516040518363ffffffff1660e01b81526004016111f09291906156d1565b815260200161136887600001516001600160a01b03166347d1b53c89604001518a608001516040518363ffffffff1660e01b815260040161128b9291906156d1565b8152600060208083019190915294850151516040820152929093015151606083015260809091015290505b919050565b602081015151611499906001600160a01b0316331480611451575081516020830151516040517f3a031bf00000000000000000000000000000000000000000000000000000000081526001600160a01b0390921691633a031bf091611401913390600401615605565b60206040518083038186803b15801561141957600080fd5b505afa15801561142d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506114519190810190614cfa565b7f4c697175696461746f7250726f787942617365000000000000000000000000007f53656e646572206e6f74206f70657261746f7200000000000000000000000000336129c5565b61010081015163ffffffff161561080a5760008160e001516001600160a01b0316631be7dd83836040015184608001516040518363ffffffff1660e01b81526004016114e69291906156d1565b60206040518083038186803b1580156114fe57600080fd5b505afa158015611512573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506115369190810190614f8f565b90506108a982610100015163ffffffff168263ffffffff16147f4c697175696461746f7250726f787942617365000000000000000000000000007f457870697279206d69736d6174636800000000000000000000000000000000008463ffffffff1686610100015163ffffffff16612a27565b60006115ca8260800151602001518360e00151612aeb90919063ffffffff16565b905060006115ee8360a0015160200151846101200151612aeb90919063ffffffff16565b90508082101561163057608083015160209081018051918501919091525160e0840151610120850151611622929190612b3f565b8352600160c084015261165f565b61164c8360a00151602001518461012001518560e00151612ba4565b60208085019190915260a0840151015183525b505050565b805160009061166f57fe5b508160001981141561168357508051610785565b81518110156107855780825261012082015160e08301516116a5918391612ba4565b602083015292915050565b60608084518660800151016040519080825280602002602001820160405280156116f457816020015b6116e1614593565b8152602001906001900390816116d95790505b5090506040518060400160405280856001600160a01b03168152602001848152508160008151811061172257fe5b60200260200101819052506040518060400160405280856001600160a01b031681526020016117518686612bba565b8152508160018151811061176157fe5b6020026020010181905250611777868683612bf0565b90505b949350505050565b60005b81518110156118475783600001516001600160a01b03166347d1b53c848484815181106117ae57fe5b60200260200101516040518363ffffffff1660e01b81526004016117d39291906156d1565b604080518083038186803b1580156117ea57600080fd5b505afa1580156117fe573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506118229190810190614ebd565b602001516fffffffffffffffffffffffffffffffff161561183f57fe5b600101611785565b50505050565b60006002815b83518110156119525761187c84828151811061186b57fe5b60200260200101516000015161255e565b156119125783818151811061188d57fe5b6020026020010151604001516001600160a01b031663b189111a6040518163ffffffff1660e01b815260040160206040518083038186803b1580156118d157600080fd5b505afa1580156118e5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506119099190810190614f29565b8201915061194a565b61193284828151811061192157fe5b602002602001015160000151612576565b156119435783818151811061188d57fe5b6001820191505b600101611853565b5092915050565b61010083015163ffffffff16156119c45761199a60006002856080015186606001518760e00151886101000151886020015189600001518a60c00151612cc0565b848260a0018051809190600101815250815181106119b457fe5b6020026020010181905250611847565b6119df60006002856080015186606001518660000151612dbd565b848260a0018051809190600101815250815181106119f957fe5b602002602001018190525050505050565b600019831415611b0857611ade6000600186600081518110611a2857fe5b602002602001015188600001516001600160a01b031663c190c2ec8c600081518110611a5057fe5b60200260200101518a600081518110611a6557fe5b60200260200101516040518363ffffffff1660e01b8152600401611a8a9291906156d1565b604080518083038186803b158015611aa157600080fd5b505afa158015611ab5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611ad99190810190614f0b565b612e3c565b868660a001805180919060010181525081518110611af857fe5b6020026020010181905250611b50565b611b2a6000600186600081518110611b1c57fe5b602002602001015186612ece565b868660a001805180919060010181525081518110611b4457fe5b60200260200101819052505b60005b8151811015612234576000828281518110611b6a57fe5b6020026020010151600001516003811115611b8157fe5b1415611c2c57611c026001868381518110611b9857fe5b6020026020010151878460010181518110611baf57fe5b6020026020010151858581518110611bc357fe5b602002602001015160400151611bd98987612f9d565b611be589888a51612fb9565b888881518110611bf157fe5b602002602001015160600151612fd4565b878760a001805180919060010181525081518110611c1c57fe5b602002602001018190525061222c565b6001828281518110611c3a57fe5b6020026020010151600001516003811115611c5157fe5b1415611d865760006060838381518110611c6757fe5b602002602001015160600151806020019051611c869190810190614f47565b91509150611ced836000148015611c9c57508683145b80611ca657508315155b7f47656e6572696354726164657250726f787942617365000000000000000000007f496e76616c696420637573746f6d20696e70757420616d6f756e740000000000612418565b611d5a60018960800151868681518110611d0357fe5b60200260200101516020015101898681518110611d1c57fe5b60200260200101518a8760010181518110611d3357fe5b6020026020010151888881518110611d4757fe5b602002602001015160400151878761309f565b898960a001805180919060010181525081518110611d7457fe5b6020026020010181905250505061222c565b611d9582828151811061186b57fe5b15611fb6578015611da257fe5b6060828281518110611db057fe5b6020026020010151604001516001600160a01b031663f93cffde60405180610160016040528060018152602001611de561311d565b81526020018c600181518110611df757fe5b6020026020010151600001516001600160a01b031681526020018c600181518110611e1e57fe5b60200260200101516020015181526020018c611e3861311d565b81518110611e4257fe5b6020026020010151600001516001600160a01b031681526020018c611e6561311d565b81518110611e6f57fe5b6020026020010151602001518152602001898660010181518110611e8f57fe5b60200260200101518152602001898681518110611ea857fe5b60200260200101518152602001611ec188878951612fb9565b8152602001611ed08987612f9d565b8152602001868681518110611ee157fe5b6020026020010151606001518152506040518263ffffffff1660e01b8152600401611f0c91906156b2565b60006040518083038186803b158015611f2457600080fd5b505afa158015611f38573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611f609190810190614c90565b905060005b8151811015611faf57818181518110611f7a57fe5b6020026020010151898960a001805180919060010181525081518110611f9c57fe5b6020908102919091010152600101611f65565b505061222c565b611fc582828151811061192157fe5b611fcb57fe5b61201c600183510382147f47656e6572696354726164657250726f787942617365000000000000000000007f57726170706572206d75737420626520746865206c6173742074726164657200612418565b606082828151811061202a57fe5b6020026020010151604001516001600160a01b0316634e2e55806040518061016001604052806001815260200161205f61311d565b81526020018c60018151811061207157fe5b6020026020010151600001516001600160a01b031681526020018c60018151811061209857fe5b60200260200101516020015181526020018c6120b261311d565b815181106120bc57fe5b6020026020010151600001516001600160a01b031681526020018c6120df61311d565b815181106120e957fe5b602002602001015160200151815260200189866001018151811061210957fe5b6020026020010151815260200189868151811061212257fe5b6020026020010151815260200161213b88878951612fb9565b815260200161214a8987612f9d565b815260200186868151811061215b57fe5b6020026020010151606001518152506040518263ffffffff1660e01b815260040161218691906156b2565b60006040518083038186803b15801561219e57600080fd5b505afa1580156121b2573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526121da9190810190614c90565b905060005b8151811015612229578181815181106121f457fe5b6020026020010151898960a00180518091906001018152508151811061221657fe5b60209081029190910101526001016121df565b50505b600101611b53565b50612261600160008660018851038151811061224c57fe5b602002602001015161225c613122565b612ece565b868660a00180518091906001018152508151811061227b57fe5b602002602001018190525050505050505050565b60006060846001600160a01b031684846040516024016122af9190615653565b60408051601f198184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090941693909317909252905161231a91906154e9565b600060405180830381855afa9150503d8060008114612355576040519150601f19603f3d011682016040523d82523d6000602084013e61235a565b606091505b5090969095509350505050565b6000606084905060608484036040519080825280601f01601f19166020018201604052801561239d576020820181803883390190505b509050845b84811015612406578281815181106123b657fe5b602001015160f81c60f81b82878303815181106123cf57fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053506001016123a2565b50805160209091012095945050505050565b8261165f5761242682613128565b7f3a2000000000000000000000000000000000000000000000000000000000000061245083613128565b604051602001612462939291906154f5565b60408051601f19818403018152908290527f08c379a000000000000000000000000000000000000000000000000000000000825261014191600401615653565b60408201516124fe906001600160a01b031615157f47656e6572696354726164657250726f787942617365000000000000000000007f496e76616c69642074726164657220617420696e6465780000000000000000008461258b565b600084828151811061250c57fe5b60200260200101519050600085836001018151811061252757fe5b6020026020010151905061253d878383876131c4565b61254a87838387876134b7565b612555858585613c2e565b50505050505050565b600081600381111561256c57fe5b60025b1492915050565b600081600381111561258457fe5b600361256f565b836118475761259983613128565b7f3a200000000000000000000000000000000000000000000000000000000000006125c384613128565b7f203c0000000000000000000000000000000000000000000000000000000000006125ed85613d28565b6040516124629594939291907f3e000000000000000000000000000000000000000000000000000000000000009060200161551d565b80516000908015610785575050602001516fffffffffffffffffffffffffffffffff16151590565b600061265642613e19565b905090565b60606001610100830401604051908082528060200260200182016040528015611952578160200160208202803883390190505092915050565b6000805b84518110156126ee576126be848683815181106126b157fe5b6020026020010151613e70565b6126e6576126df848683815181106126d257fe5b6020026020010151613eb0565b6001830192505b600101612698565b50909392505050565b600060ff6fffffffffffffffffffffffffffffffff83161561273a577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8001612742565b608083901c92505b67ffffffffffffffff831615612779577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc001612781565b604083901c92505b63ffffffff83161561279657601f190161279e565b602083901c92505b61ffff8316156127cf577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016127d7565b601083901c92505b60ff831615612807577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80161280f565b600883901c92505b600f83161561283f577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc01612847565b600483901c92505b6003831615612877577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0161287f565b600283901c92505b6001831615610785576000190192915050565b610100919091020190565b6001901b900390565b6128ae6145aa565b610fc3836000855185613ee6565b6000610fc3838360000151670de0b6b3a7640000612ba4565b600082820183811015610fc3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161014190615672565b61291c614593565b602083015183516fffffffffffffffffffffffffffffffff90911690156129885760408051808201909152600181526020848101519082019061297d9084906bffffffffffffffffffffffff16670de0b6b3a764000063ffffffff613fd016565b815250915050610785565b60408051808201909152600081528351602082019061297d9084906bffffffffffffffffffffffff16670de0b6b3a764000063ffffffff613fd016565b83611847576129d383613128565b7f3a200000000000000000000000000000000000000000000000000000000000006129fd84613128565b7f203c0000000000000000000000000000000000000000000000000000000000006125ed8561405a565b8461093357612a3584613128565b7f3a20000000000000000000000000000000000000000000000000000000000000612a5f85613128565b7f203c000000000000000000000000000000000000000000000000000000000000612a8986613d28565b7f2c20000000000000000000000000000000000000000000000000000000000000612ab387613d28565b60405161246297969594939291907f3e000000000000000000000000000000000000000000000000000000000000009060200161557b565b600082612afa57506000610785565b82820282848281612b0757fe5b0414610fc3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161014190615692565b6000831580612b4c575082155b15612b6357612b5c6000836141c4565b9050610fc3565b61177a6001612b9884612b8c83612b808a8a63ffffffff612aeb16565b9063ffffffff61420616565b9063ffffffff6141c416565b9063ffffffff6128d516565b600061177a82612b8c868663ffffffff612aeb16565b6000828242604051602001612bd19392919061549d565b60408051601f1981840301815291905280516020909101209392505050565b60005b825181101561184757612c04614593565b828286608001510181518110612c1657fe5b6020026020010151905060006001600160a01b031681600001516001600160a01b0316148015612c4857506020810151155b612c4e57fe5b6040518060400160405280858481518110612c6557fe5b6020026020010151600001516001600160a01b03168152602001858481518110612c8b57fe5b602002602001015160200151815250838387608001510181518110612cac57fe5b602090810291909101015250600101612bf3565b612cc8614541565b612cd06145e4565b82612d00576040805160808101909152600181526020810160008152602001600081526020018590529050612d2e565b82612d0757fe5b60408051608081019091526000808252602082019081526020016000815260200186905290505b60408051610100810190915280600581526020018c81526020018281526020018415612d5a5789612d5c565b8a5b81526020018415612d6d578a612d6f565b895b8152602001886001600160a01b031681526020018b81526020018a88604051602001612d9c929190615778565b60408051601f1981840301815291905290529b9a5050505050505050505050565b612dc5614541565b50604080516101008101825260068152602080820197909752815160808082018452600182526000828a0181905282850181905260608084019690965283850192909252938201959095529182019290925260a0810183905260c0810193909352805191825292810190925260e081019190915290565b612e44614541565b60408051610100810190915280600281526020808201889052604080516080810182528651151581529201919081016000815260200160018152602001856020015181525081526020018481526020016000815260200160006001600160a01b03168152602001858152602001604051806020016040528060008152508152509050949350505050565b612ed6614541565b612ede6145e4565b600019831415612f155760408051608081019091526001815260208101600081526020016001815260200160008152509050612f3c565b60408051608081019091526000808252602082019081526020016000815260200184905290505b60408051610100810190915280600281526020018781526020018281526020018581526020016000815260200160006001600160a01b0316815260200186815260200160405180602001604052806000815250815250915050949350505050565b60008115612fb257612fad613122565b610fc3565b5090919050565b6000600182038314612fcc57600161177a565b509192915050565b612fdc614541565b60408051610100810182526004815260208082018b905282516080810184526000808252929384019290918201908152602001600019881461301f576000613022565b60015b600181111561302d57fe5b815260200160001988146130415787613044565b60005b8152508152602001888152602001878152602001866001600160a01b03168152602001600081526020018484604051602001613081929190615715565b60408051601f19818403018152919052905298975050505050505050565b6130a7614541565b604080516101008101909152806005815260208082018b905260408051608081018252600181529201919081016000815260200160008152602001868152508152602001878152602001868152602001856001600160a01b03168152602001888152602001838152509050979650505050505050565b600290565b60001990565b6060808260405160200161313c91906154d4565b60408051601f19818403018152919052905060205b80156131a95781516000199091019082908290811061316c57fe5b01602001517fff0000000000000000000000000000000000000000000000000000000000000016156131a45760010181529050611393565b613151565b5060408051600080825260208201909252905b509392505050565b83516131d090846105dc565b156133bf576132406131e5826000015161255e565b7f47656e6572696354726164657250726f787942617365000000000000000000007f496e76616c69642069736f6c6174696f6e206d6f646520756e77726170706572868560000151600381111561323857fe5b60ff16612a27565b835161324c90836105dc565b156133ba5783516040517f062bd3e90000000000000000000000000000000000000000000000000000000081526000916001600160a01b03169063062bd3e99061329a9086906004016156ec565b60206040518083038186803b1580156132b257600080fd5b505afa1580156132c6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506132ea9190810190614c72565b90506133b8816001600160a01b03166316774ba084604001516040518263ffffffff1660e01b815260040161331f91906155f7565b60206040518083038186803b15801561333757600080fd5b505afa15801561334b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061336f9190810190614cfa565b7f47656e6572696354726164657250726f787942617365000000000000000000007f496e76616c696420756e777261702073657175656e63650000000000000000008787612a27565b505b611847565b83516133cb90836105dc565b15613433576133ba6133e08260000151612576565b7f47656e6572696354726164657250726f787942617365000000000000000000007f496e76616c69642069736f6c6174696f6e206d6f646520777261707065720000858560000151600381111561323857fe5b61184760008251600381111561344557fe5b148061345d575060018251600381111561345b57fe5b145b7f47656e6572696354726164657250726f787942617365000000000000000000007f496e76616c696420747261646572207479706500000000000000000000000000846000015160038111156134af57fe5b60ff1661258b565b81516134c29061255e565b1561387657604080830151865191517f062bd3e900000000000000000000000000000000000000000000000000000000815290916000916001600160a01b039091169063062bd3e9906135199089906004016156ec565b60206040518083038186803b15801561353157600080fd5b505afa158015613545573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506135699190810190614c72565b905061363c816001600160a01b0316836001600160a01b031663fc0c546a6040518163ffffffff1660e01b815260040160206040518083038186803b1580156135b157600080fd5b505afa1580156135c5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506135e99190810190614c72565b6001600160a01b0316147f47656e6572696354726164657250726f787942617365000000000000000000007f496e76616c696420696e70757420666f7220756e777261707065720000000000868a612a27565b613786826001600160a01b031663f1a1f8fa89600001516001600160a01b031663062bd3e9896040518263ffffffff1660e01b815260040161367e91906156ec565b60206040518083038186803b15801561369657600080fd5b505afa1580156136aa573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506136ce9190810190614c72565b6040518263ffffffff1660e01b81526004016136ea91906155f7565b60206040518083038186803b15801561370257600080fd5b505afa158015613716573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061373a9190810190614cfa565b7f47656e6572696354726164657250726f787942617365000000000000000000007f496e76616c6964206f757470757420666f7220756e77726170706572000000008660010189612a27565b60408085015190517f16774ba000000000000000000000000000000000000000000000000000000000815261386f916001600160a01b038416916316774ba0916137d2916004016155f7565b60206040518083038186803b1580156137ea57600080fd5b505afa1580156137fe573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506138229190810190614cfa565b7f47656e6572696354726164657250726f787942617365000000000000000000007f556e7772617070657220747261646572206e6f7420656e61626c65640000000087604001518a614248565b5050610933565b815161388190612576565b1561093357604080830151865191517f062bd3e900000000000000000000000000000000000000000000000000000000815290916000916001600160a01b039091169063062bd3e9906138d89088906004016156ec565b60206040518083038186803b1580156138f057600080fd5b505afa158015613904573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506139289190810190614c72565b9050613a71826001600160a01b0316639f4a129789600001516001600160a01b031663062bd3e98a6040518263ffffffff1660e01b815260040161396c91906156ec565b60206040518083038186803b15801561398457600080fd5b505afa158015613998573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506139bc9190810190614c72565b6040518263ffffffff1660e01b81526004016139d891906155f7565b60206040518083038186803b1580156139f057600080fd5b505afa158015613a04573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250613a289190810190614cfa565b7f47656e6572696354726164657250726f787942617365000000000000000000007f496e76616c696420696e70757420666f72207772617070657200000000000000868a612a27565b613b45816001600160a01b0316836001600160a01b031663fc0c546a6040518163ffffffff1660e01b815260040160206040518083038186803b158015613ab757600080fd5b505afa158015613acb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250613aef9190810190614c72565b6001600160a01b0316147f47656e6572696354726164657250726f787942617365000000000000000000007f496e76616c6964206f757470757420666f7220777261707065720000000000008660010189612a27565b60408085015190517f16774ba0000000000000000000000000000000000000000000000000000000008152612555916001600160a01b038416916316774ba091613b91916004016155f7565b60206040518083038186803b158015613ba957600080fd5b505afa158015613bbd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250613be19190810190614cfa565b7f47656e6572696354726164657250726f787942617365000000000000000000007f5772617070657220747261646572206e6f7420656e61626c6564000000000000876040015189614248565b81516003811115613c3b57fe5b60011415613cd557613cd083518360200151108015613c88575060006001600160a01b031684846020015181518110613c7057fe5b6020026020010151600001516001600160a01b031614155b7f47656e6572696354726164657250726f787942617365000000000000000000007f496e76616c6964206d616b6572206163636f756e74206f776e657200000000008461258b565b61165f565b61165f82602001516000147f47656e6572696354726164657250726f787942617365000000000000000000007f496e76616c6964206d616b6572206163636f756e74206f776e657200000000008461258b565b606081613d69575060408051808201909152600181527f30000000000000000000000000000000000000000000000000000000000000006020820152611393565b8160005b8115613d8157600101600a82049150613d6d565b6060816040519080825280601f01601f191660200182016040528015613dae576020820181803883390190505b508593509050815b8015613e105760001901600a840660300160f81b828281518110613dd657fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350600a84049350613db6565b50949350505050565b60008161078563ffffffff821682147f4d617468000000000000000000000000000000000000000000000000000000007f556e73616665206361737420746f2075696e74333200000000000000000000008461258b565b600080610100830490506000610100840690506000816001901b868481518110613e9657fe5b602002602001015116905060008111935050505092915050565b815161010082049060ff8316906001821b90859084908110613ece57fe5b60200260200101818151179150818152505050505050565b613eee6145aa565b838303801580613f205750806001148015613f20575082868681518110613f1157fe5b60200260200101516000015114155b15613f57576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161014190615682565b600060028204860190506000878281518110613f6f57fe5b602002602001015160000151905080851015613f9b57613f9188888488613ee6565b935050505061177a565b80851115613fb257613f9188836001018888613ee6565b878281518110613fbe57fe5b6020026020010151935050505061177a565b6000831580613fdd575082155b15613fed57612b5c6000836141c4565b6000613fff858563ffffffff612aeb16565b905061405161401e6001612b986002612b8c888463ffffffff61420616565b61402e838663ffffffff6142aa16565b101561403b57600061403e565b60015b60ff16612b98838663ffffffff6141c416565b95945050505050565b60408051602a80825260608281019093526001600160a01b038416918391602082018180388339019050509050603060f81b8160008151811061409957fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350607860f81b816001815181106140da57fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535060005b60148110156131bc5760028102614125600f85166142ec565b83826029038151811061413457fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350600484901c9350614176600f85166142ec565b83826028038151811061418557fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053505060049290921c9160010161410c565b6000610fc383836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f00000000000081525061430e565b6000610fc383836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f77000081525061435f565b846109335761425684613128565b7f3a2000000000000000000000000000000000000000000000000000000000000061428085613128565b7f203c000000000000000000000000000000000000000000000000000000000000612a898661405a565b6000610fc383836040518060400160405280601881526020017f536166654d6174683a206d6f64756c6f206279207a65726f00000000000000008152506143a5565b6000600a82101561430457506030810160f81b611393565b5060570160f81b90565b60008183614349576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016101419190615653565b50600083858161435557fe5b0495945050505050565b6000818484111561439d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016101419190615653565b505050900390565b600081836143e0576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016101419190615653565b508284816143ea57fe5b06949350505050565b60405180610120016040528060006001600160a01b0316815260200160006001600160a01b03168152602001600015158152602001600081526020016000815260200160008152602001614445614593565b8152602001614452614593565b815260200161445f614593565b905290565b60405180610120016040528060006001600160a01b03168152602001614488614593565b8152602001614495614593565b81526020016000815260200160008152602001606081526020016060815260200160006001600160a01b03168152602001600063ffffffff1681525090565b60405180610140016040528060008152602001600081526020016144f6614593565b8152602001614503614593565b8152602001614510614593565b815260200161451d614593565b81526020016000151581526020016000815260200160008152602001600081525090565b6040805161010081018252600080825260208201529081016145616145e4565b8152602001600081526020016000815260200160006001600160a01b0316815260200160008152602001606081525090565b604080518082019091526000808252602082015290565b6040518060600160405280600081526020016145c46145d1565b815260200161445f61460d565b6040518060200160405280600081525090565b604080516080810190915260008082526020820190815260200160008152602001600081525090565b604080516060810182526000808252602082018190529181019190915290565b80356107858161595b565b80516107858161595b565b600082601f83011261465457600080fd5b8151614667614662826157ba565b615793565b81815260209384019390925082018360005b838110156146a5578151860161468f888261492a565b8452506020928301929190910190600101614679565b5050505092915050565b600082601f8301126146c057600080fd5b81356146ce614662826157ba565b915081818352602084019350602081019050838560408402820111156146f357600080fd5b60005b838110156146a557816147098882614b03565b845250602090920191604091909101906001016146f6565b600082601f83011261473257600080fd5b8135614740614662826157ba565b81815260209384019390925082018360005b838110156146a557813586016147688882614b85565b8452506020928301929190910190600101614752565b600082601f83011261478f57600080fd5b813561479d614662826157ba565b915081818352602084019350602081019050838560208402820111156147c257600080fd5b60005b838110156146a557816147d88882614c46565b84525060209283019291909101906001016147c5565b600082601f8301126147ff57600080fd5b815161480d614662826157ba565b9150818183526020840193506020810190508385602084028201111561483257600080fd5b60005b838110156146a557816148488882614c51565b8452506020928301929190910190600101614835565b80516107858161596f565b600082601f83011261487a57600080fd5b8135614888614662826157db565b915080825260208301602083018583830111156148a457600080fd5b6148af8382846158ee565b50505092915050565b600082601f8301126148c957600080fd5b81516148d7614662826157db565b915080825260208301602083018583830111156148f357600080fd5b6148af8382846158fa565b803561078581615978565b805161078581615981565b80516107858161598e565b80356107858161599b565b6000610160828403121561493d57600080fd5b614948610100615793565b905060006149568484614909565b825250602061496784848301614c51565b602083015250604061497b84828501614a06565b60408301525060c061498f84828501614c51565b60608301525060e06149a384828501614c51565b6080830152506101006149b884828501614638565b60a0830152506101206149cd84828501614c51565b60c08301525061014082015167ffffffffffffffff8111156149ee57600080fd5b6149fa848285016148b8565b60e08301525092915050565b600060808284031215614a1857600080fd5b614a226080615793565b90506000614a30848461485e565b8252506020614a4184848301614914565b6020830152506040614a5584828501614914565b6040830152506060614a6984828501614c51565b60608301525092915050565b600060208284031215614a8757600080fd5b614a916020615793565b90506000614a9f8484614c51565b82525092915050565b600060608284031215614aba57600080fd5b614ac46060615793565b90506000614ad28484614c67565b8252506020614ae384848301614c67565b6020830152506040614af784828501614c5c565b60408301525092915050565b600060408284031215614b1557600080fd5b614b1f6040615793565b90506000614b2d848461462d565b8252506020614b3e84848301614c46565b60208301525092915050565b600060408284031215614b5c57600080fd5b614b666040615793565b90506000614b74848461485e565b8252506020614b3e84848301614c3b565b600060808284031215614b9757600080fd5b614ba16080615793565b90506000614baf848461491f565b8252506020614bc084848301614c46565b6020830152506040614bd48482850161462d565b604083015250606082013567ffffffffffffffff811115614bf457600080fd5b614a6984828501614869565b600060408284031215614c1257600080fd5b614c1c6040615793565b90506000614c2a848461485e565b8252506020614b3e84848301614c51565b8051610785816159a8565b8035610785816159b1565b8051610785816159b1565b8051610785816159ba565b8051610785816159c3565b600060208284031215614c8457600080fd5b600061177a8484614638565b600060208284031215614ca257600080fd5b815167ffffffffffffffff811115614cb957600080fd5b61177a84828501614643565b600060208284031215614cd757600080fd5b815167ffffffffffffffff811115614cee57600080fd5b61177a848285016147ee565b600060208284031215614d0c57600080fd5b600061177a848461485e565b60008060408385031215614d2b57600080fd5b6000614d3785856148fe565b9250506020614d4885828601614c46565b9150509250929050565b600060208284031215614d6457600080fd5b815167ffffffffffffffff811115614d7b57600080fd5b61177a848285016148b8565b600060208284031215614d9957600080fd5b600061177a8484614a75565b600060608284031215614db757600080fd5b600061177a8484614aa8565b600080600080600080600080610140898b031215614de057600080fd5b6000614dec8b8b614b03565b9850506040614dfd8b828c01614b03565b975050608089013567ffffffffffffffff811115614e1a57600080fd5b614e268b828c0161477e565b96505060a0614e378b828c01614c46565b95505060c0614e488b828c01614c46565b94505060e089013567ffffffffffffffff811115614e6557600080fd5b614e718b828c01614721565b93505061010089013567ffffffffffffffff811115614e8f57600080fd5b614e9b8b828c016146af565b925050610120614ead8b828c01614c46565b9150509295985092959890939650565b600060408284031215614ecf57600080fd5b600061177a8484614b4a565b60008060408385031215614eee57600080fd5b6000614efa8585614a75565b9250506020614d4885828601614a75565b600060408284031215614f1d57600080fd5b600061177a8484614c00565b600060208284031215614f3b57600080fd5b600061177a8484614c51565b60008060408385031215614f5a57600080fd5b6000614f668585614c51565b925050602083015167ffffffffffffffff811115614f8357600080fd5b614d48858286016148b8565b600060208284031215614fa157600080fd5b600061177a8484614c5c565b6000610fc383836152a1565b6000614fc5838361546b565b505060400190565b614fd6816158cd565b82525050565b614fd681615816565b614fd6614ff182615816565b615926565b600061500182615809565b61500b818561580d565b93508360208202850161501d85615803565b8060005b85811015615057578484038952815161503a8582614fad565b945061504583615803565b60209a909a0199925050600101615021565b5091979650505050505050565b600061506f82615809565b615079818561580d565b935061508483615803565b8060005b838110156150b257815161509c8882614fb9565b97506150a783615803565b925050600101615088565b509495945050505050565b614fd681615821565b614fd66150d282615826565b615870565b614fd66150d28261584b565b614fd66150d282615870565b60006150fa82615809565b615104818561580d565b93506151148185602086016158fa565b61511d81615937565b9093019392505050565b600061513282615809565b61513c8185611393565b935061514c8185602086016158fa565b9290920192915050565b614fd681615873565b614fd6816158d8565b614fd6816158e3565b600061517e601b8361580d565b7f536166654d6174683a206164646974696f6e206f766572666c6f770000000000815260200192915050565b60006151b760258361580d565b7f4c697175696461746f7250726f7879426173653a204d61726b6574206e6f742081527f666f756e64000000000000000000000000000000000000000000000000000000602082015260400192915050565b600061521660218361580d565b7f536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f81527f7700000000000000000000000000000000000000000000000000000000000000602082015260400192915050565b6000615275601f8361580d565b7f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00815260200192915050565b80516000906101608401906152b6858261515f565b5060208301516152c9602086018261548b565b5060408301516152dc6040860182615343565b5060608301516152ef60c086018261548b565b50608083015161530260e086018261548b565b5060a0830151615316610100860182614fdc565b5060c083015161532a61012086018261548b565b5060e083015184820361014086015261405182826150ef565b8051608083019061535484826150bd565b5060208201516153676020850182615168565b50604082015161537a6040850182615168565b506060820151611847606085018261548b565b80516000906101608401906153a2858261548b565b5060208301516153b5602086018261548b565b5060408301516153c86040860182614fdc565b5060608301516153db606086018261548b565b5060808301516153ee6080860182614fdc565b5060a083015161540160a086018261548b565b5060c083015161541460c086018261548b565b5060e083015161542760e086018261548b565b5061010083015161543c61010086018261548b565b5061012083015161545161012086018261548b565b5061014083015184820361014086015261405182826150ef565b8051604083019061547c8482614fdc565b50602082015161184760208501825b614fd681615870565b614fd6816158b3565b60006154a98286614fe5565b6014820191506154b982856150e3565b6020820191506154c982846150e3565b506020019392505050565b60006154e082846150e3565b50602001919050565b6000610fc38284615127565b60006155018286615127565b915061550d82856150d7565b6002820191506140518284615127565b60006155298289615127565b915061553582886150d7565b6002820191506155458287615127565b915061555182866150d7565b6002820191506155618285615127565b915061556d82846150c6565b506001019695505050505050565b6000615587828b615127565b9150615593828a6150d7565b6002820191506155a38289615127565b91506155af82886150d7565b6002820191506155bf8287615127565b91506155cb82866150d7565b6002820191506155db8285615127565b91506155e782846150c6565b5060010198975050505050505050565b602081016107858284614fdc565b604081016156138285614fdc565b610fc36020830184614fcd565b604080825281016156318185615064565b9050818103602083015261177a8184614ff6565b6020810161078582846150bd565b60208082528101610fc381846150ef565b602081016107858284615156565b6020808252810161078581615171565b60208082528101610785816151aa565b6020808252810161078581615209565b6020808252810161078581615268565b60208082528101610fc3818461538d565b60408101610785828461546b565b606081016156df828561546b565b610fc3604083018461548b565b60208101610785828461548b565b60408101615708828561548b565b610fc36020830184614fdc565b60408101615723828561548b565b818103602083015261177a81846150ef565b60408101615743828561548b565b610fc3602083018461548b565b6060810161575e828661548b565b61576b602083018561548b565b61177a6040830184615494565b60408101615786828561548b565b610fc36020830184615494565b60405181810167ffffffffffffffff811182821017156157b257600080fd5b604052919050565b600067ffffffffffffffff8211156157d157600080fd5b5060209081020190565b600067ffffffffffffffff8211156157f257600080fd5b506020601f91909101601f19160190565b60200190565b5190565b90815260200190565b6000610785826158a7565b151590565b7fff000000000000000000000000000000000000000000000000000000000000001690565b7fffff0000000000000000000000000000000000000000000000000000000000001690565b90565b600061078582615816565b8061139381615947565b8061139381615951565b6fffffffffffffffffffffffffffffffff1690565b6001600160a01b031690565b63ffffffff1690565b6bffffffffffffffffffffffff1690565b600061078582615873565b60006107858261587e565b600061078582615888565b82818337506000910152565b60005b838110156159155781810151838201526020016158fd565b838111156118475750506000910152565b600061078582600061078582615941565b601f01601f191690565b60601b90565b6009811061080a57fe5b6002811061080a57fe5b61596481615816565b811461080a57600080fd5b61596481615821565b61596481615873565b6009811061080a57600080fd5b6002811061080a57600080fd5b6004811061080a57600080fd5b61596481615892565b61596481615870565b615964816158b3565b615964816158bc56fea365627a7a72315820a1f7f7c0adea7cfb6f73fc2c0fbce4e3dcf6446017c30ef0a13f998b1e39b1cd6c6578706572696d656e74616cf564736f6c63430005100040
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000dec1ae3b570ac3c57871bbd7bfeacc807f973bea0000000000000000000000006bd780e7fdf01d77e4d475c821f1e7ae0540907200000000000000000000000010d98759762efac656bd4be7f2f5599208f44fac
-----Decoded View---------------
Arg [0] : _expiryProxy (address): 0xDEc1ae3b570ac3c57871BBD7bFeacC807f973Bea
Arg [1] : _dolomiteMargin (address): 0x6Bd780E7fDf01D77e4d475c821f1e7AE05409072
Arg [2] : _liquidatorAssetRegistry (address): 0x10d98759762EFaC656BD4bE7F2f5599208F44FAc
-----Encoded View---------------
3 Constructor Arguments found :
Arg [0] : 000000000000000000000000dec1ae3b570ac3c57871bbd7bfeacc807f973bea
Arg [1] : 0000000000000000000000006bd780e7fdf01d77e4d475c821f1e7ae05409072
Arg [2] : 00000000000000000000000010d98759762efac656bd4be7f2f5599208f44fac
Net Worth in USD
Net Worth in ETH
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.