From b2b5e42d555153905175cd7f68c94137838fd468 Mon Sep 17 00:00:00 2001 From: Bogdan Dumitru Date: Wed, 1 Apr 2020 09:12:30 +0200 Subject: [PATCH] dydx WIP --- contracts/CompoundCollector.sol | 7 +- contracts/DYDXCollector.sol | 91 ++++++ contracts/MakerCollector.sol | 2 + contracts/interfaces/IERC20.sol | 7 + contracts/interfaces/dydx/ISoloMargin.sol | 19 ++ contracts/lib/{ => compound}/CarefulMath.sol | 0 contracts/lib/{ => compound}/Exponential.sol | 0 contracts/lib/dydx/Account.sol | 44 +++ contracts/lib/dydx/Decimal.sol | 86 ++++++ contracts/lib/dydx/Interest.sol | 194 +++++++++++++ contracts/lib/dydx/Math.sol | 144 ++++++++++ contracts/lib/dydx/Monetary.sol | 44 +++ contracts/lib/dydx/Require.sol | 7 + contracts/lib/dydx/SafeMath.sol | 150 ++++++++++ contracts/lib/dydx/Time.sol | 42 +++ contracts/lib/dydx/Types.sol | 287 +++++++++++++++++++ 16 files changed, 1122 insertions(+), 2 deletions(-) create mode 100644 contracts/DYDXCollector.sol create mode 100644 contracts/interfaces/IERC20.sol create mode 100644 contracts/interfaces/dydx/ISoloMargin.sol rename contracts/lib/{ => compound}/CarefulMath.sol (100%) rename contracts/lib/{ => compound}/Exponential.sol (100%) create mode 100644 contracts/lib/dydx/Account.sol create mode 100644 contracts/lib/dydx/Decimal.sol create mode 100644 contracts/lib/dydx/Interest.sol create mode 100644 contracts/lib/dydx/Math.sol create mode 100644 contracts/lib/dydx/Monetary.sol create mode 100644 contracts/lib/dydx/Require.sol create mode 100644 contracts/lib/dydx/SafeMath.sol create mode 100644 contracts/lib/dydx/Time.sol create mode 100644 contracts/lib/dydx/Types.sol diff --git a/contracts/CompoundCollector.sol b/contracts/CompoundCollector.sol index 5ad8997..019bdb3 100644 --- a/contracts/CompoundCollector.sol +++ b/contracts/CompoundCollector.sol @@ -1,17 +1,20 @@ pragma solidity >=0.4.25 <0.7.0; pragma experimental ABIEncoderV2; +import "@openzeppelin/contracts/math/SafeMath.sol"; + import "./interfaces/compound/IPriceOracle.sol"; import "./interfaces/compound/IComptroller.sol"; import "./interfaces/compound/ICToken.sol"; import "./IDefiPlatformCollector.sol"; -import "./lib/Exponential.sol"; -import "./lib/CarefulMath.sol"; +import "./lib/compound/Exponential.sol"; import "./lib/PositionsHelper.sol"; import "./lib/DependencyRegistry.sol"; contract CompoundCollector is IDefiPlatformCollector, Ownable, DependencyRegistry, Exponential, PositionsHelper { + using SafeMath for uint256; + bytes32 platformID_ = 0x436f6d706f756e64000000000000000000000000000000000000000000000000; // Compound function isDefiPlatformCollector() public pure returns (bool) { return true; } function platformID() public view returns (bytes32) { return platformID_; } diff --git a/contracts/DYDXCollector.sol b/contracts/DYDXCollector.sol new file mode 100644 index 0000000..d0a355b --- /dev/null +++ b/contracts/DYDXCollector.sol @@ -0,0 +1,91 @@ +pragma solidity >=0.4.25 <0.7.0; +pragma experimental ABIEncoderV2; + +import "@openzeppelin/contracts/math/SafeMath.sol"; + +import "./interfaces/dydx/ISoloMargin.sol"; +import "./interfaces/IERC20.sol"; + +import "./IDefiPlatformCollector.sol"; +import "./lib/PositionsHelper.sol"; +import "./lib/DependencyRegistry.sol"; + +contract DYDXCollector is IDefiPlatformCollector, Ownable, DependencyRegistry, PositionsHelper { + using SafeMath for uint256; + + bytes32 platformID_ = 0x6459645800000000000000000000000000000000000000000000000000000000; // dYdX + function isDefiPlatformCollector() public pure returns (bool) { return true; } + function platformID() public view returns (bytes32) { return platformID_; } + + uint8 constant ISoloMarginIndex = 0; + + constructor(address[] memory initialDeps) DependencyRegistry(initialDeps, 1) Ownable() public {} + + function getPositionID(uint256 nonce, address market) internal pure returns (bytes memory) { + return abi.encode(nonce, market); + } + + function getPositionCurrency(address market) internal view returns (bytes memory) { + IERC20 = token = IERC20(market); + return abi.encode(asset, token.name()); + } + + function getPoolLiquidity(address market) internal view returns (uint) { + IERC20 token = IERC20(market); + return token.balanceOf(getDependency(ISoloMarginIndex)); + } + + function getSupply(address target, uint256 marketId, Types.Wei amount) internal view returns (Defi.Position memory) { + ISoloMargin soloMargin = ISoloMargin(getDependency(ISoloMarginIndex)); + address market = soloMargin.getMarketTokenAddress(marketId); + + return Defi.Position( + getPositionID(market), + getPositionCurrency(market), + amount.value, + getPoolLiquidity(market), + 0, + abi.encode(soloMargin.getMarketTotalPar(marketId)) + ); + } + + function getBorrow(address target, uint256 marketId, Types.Wei amount) internal view returns (Defi.Position memory) { + return Defi.Position( + getPositionID(asset), + getPositionCurrency(asset), + amount.value, + 0, + 0, + abi.encode(soloMargin.getMarketTotalPar(marketId)) + ); + } + + function getPositions(address target) public view returns (Defi.PlatformResult memory) { + ISoloMargin soloMargin = ISoloMargin(getDependency(ISoloMarginIndex)); + Account.Info account = Account.Info(target, 0); + uint256 numMarkets = soloMargin.getNumMarkets(); + Defi.Position[] supplies = new Defi.Positions[](markets.length); + Defi.Position[] borrows = new Defi.Positions[](markets.length); + uint8 supplyIndex = 0; + uint8 borrowIndex = 0; + + for (uint8 mi = 0; mi < numMarkets; mi++) { + Types.Wei balance = soloMargin.getAccountWei(account, mi); + if (balance.value != 0) { + if (balance.sign == true) { + supplies[supplyIndex] = getSupply(target, mi, balance); + supplyIndex += 1; + } else { + borrows[borrowIndex] = getBorrow(target, mi, balance); + borrowIndex += 1; + } + } + } + + return Defi.PlatformResult( + platformID_, + repackPositions(borrows, borrowIndex), + repackPositions(supplies, supplyIndex) + ); + } +} diff --git a/contracts/MakerCollector.sol b/contracts/MakerCollector.sol index c84bd58..580caf5 100644 --- a/contracts/MakerCollector.sol +++ b/contracts/MakerCollector.sol @@ -2,6 +2,7 @@ pragma solidity >=0.4.25 <0.7.0; pragma experimental ABIEncoderV2; import "@openzeppelin/contracts/ownership/Ownable.sol"; +import "@openzeppelin/contracts/math/SafeMath.sol"; import "./interfaces/maker/IProxyRegistry.sol"; import "./interfaces/maker/IGetCdps.sol"; @@ -15,6 +16,7 @@ import "./lib/DependencyRegistry.sol"; import "./lib/PositionsHelper.sol"; contract MakerCollector is IDefiPlatformCollector, Ownable, DependencyRegistry, PositionsHelper { + using SafeMath for uint256; bytes32 platformID_ = 0x4d616b65724d4344000000000000000000000000000000000000000000000000; // MakerMCD function isDefiPlatformCollector() public pure returns (bool) { return true; } function platformID() public view returns (bytes32) { return platformID_; } diff --git a/contracts/interfaces/IERC20.sol b/contracts/interfaces/IERC20.sol new file mode 100644 index 0000000..168ea24 --- /dev/null +++ b/contracts/interfaces/IERC20.sol @@ -0,0 +1,7 @@ +pragma solidity >=0.4.25 <0.7.0; +pragma solidity ^0.4.0; + +interface IERC20 { + function name() external view returns (string); + function balanceOf(address target) external view returns (uint256); +} diff --git a/contracts/interfaces/dydx/ISoloMargin.sol b/contracts/interfaces/dydx/ISoloMargin.sol new file mode 100644 index 0000000..0a20ff5 --- /dev/null +++ b/contracts/interfaces/dydx/ISoloMargin.sol @@ -0,0 +1,19 @@ +pragma solidity >=0.4.25 <0.7.0; +pragma experimental ABIEncoderV2; + +import { Account } from "../../lib/dydx/Account.sol"; +import { Monetary } from "../../lib/dydx/Monetary.sol"; +import { Types } from "../../lib/dydx/Types.sol"; +import { Interest } from "../../lib/dydx/Interest.sol"; + +interface ISoloMargin { + function getNumMarkets() external view returns (uint256); + function getAccountValues(Account.Info calldata account) external view returns (Monetary.Value memory, Monetary.Value memory); + function getAccountStatus(Account.Info calldata account) external view returns (Account.Status); + function getAccountWei(Account.Info calldata account, uint256 marketId) external view returns (Types.Wei); + function getMarketTokenAddress(uint256 marketId) external view returns (address); + function getMarketCurrentIndex(uint256 marketId) external view returns (Interest.Index memory); + function getMarketInterestRate(uint256 marketId) external view returns (Interest.Rate memory); + function getMarketTotalPar(uint256 marketId) external view returns (Types.TotalPar memory); +} + diff --git a/contracts/lib/CarefulMath.sol b/contracts/lib/compound/CarefulMath.sol similarity index 100% rename from contracts/lib/CarefulMath.sol rename to contracts/lib/compound/CarefulMath.sol diff --git a/contracts/lib/Exponential.sol b/contracts/lib/compound/Exponential.sol similarity index 100% rename from contracts/lib/Exponential.sol rename to contracts/lib/compound/Exponential.sol diff --git a/contracts/lib/dydx/Account.sol b/contracts/lib/dydx/Account.sol new file mode 100644 index 0000000..1c064cd --- /dev/null +++ b/contracts/lib/dydx/Account.sol @@ -0,0 +1,44 @@ +pragma solidity >0.5.7; +pragma experimental ABIEncoderV2; + +import { Types } from "./Types.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 { + mapping (uint256 => Types.Par) balances; // Mapping from marketId to principal + Status status; + } +} diff --git a/contracts/lib/dydx/Decimal.sol b/contracts/lib/dydx/Decimal.sol new file mode 100644 index 0000000..d66ae66 --- /dev/null +++ b/contracts/lib/dydx/Decimal.sol @@ -0,0 +1,86 @@ +/* + + 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-solidity/contracts/math/SafeMath.sol"; +import { Math } from "./Math.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 Math.getPartial(target, d.value, BASE); + } + + function div( + uint256 target, + D256 memory d + ) + internal + pure + returns (uint256) + { + return Math.getPartial(target, BASE, d.value); + } +} diff --git a/contracts/lib/dydx/Interest.sol b/contracts/lib/dydx/Interest.sol new file mode 100644 index 0000000..a3af801 --- /dev/null +++ b/contracts/lib/dydx/Interest.sol @@ -0,0 +1,194 @@ +/* + + 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-solidity/contracts/math/SafeMath.sol"; +import { Decimal } from "./Decimal.sol"; +import { Math } from "./Math.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 Solo + */ +library Interest { + using Math for uint256; + using SafeMath for uint256; + + // ============ Constants ============ + + bytes32 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 the 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) { + supplyInterest = Math.getPartial(supplyInterest, borrowWei.value, supplyWei.value); + } + } + assert(supplyInterest <= borrowInterest); + + return Index({ + borrow: Math.getPartial(index.borrow, borrowInterest, BASE).add(index.borrow).to96(), + supply: Math.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.getPartial(index.supply, BASE) + }); + } else { + return Types.Wei({ + sign: false, + value: inputValue.getPartialRoundUp(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.getPartial(BASE, index.supply).to128() + }); + } else { + return Types.Par({ + sign: false, + value: input.value.getPartialRoundUp(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); + } +} diff --git a/contracts/lib/dydx/Math.sol b/contracts/lib/dydx/Math.sol new file mode 100644 index 0000000..4a5e399 --- /dev/null +++ b/contracts/lib/dydx/Math.sol @@ -0,0 +1,144 @@ +/* + + 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 "./SafeMath.sol"; +import { Require } from "./Require.sol"; + + +/** + * @title Math + * @author dYdX + * + * Library for non-standard Math functions + */ +library Math { + using SafeMath for uint256; + + // ============ Constants ============ + + bytes32 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 up. + */ + 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); + } + + function to128( + uint256 number + ) + internal + pure + returns (uint128) + { + uint128 result = uint128(number); + Require.that( + result == number, + FILE, + "Unsafe cast to uint128" + ); + return result; + } + + function to96( + uint256 number + ) + internal + pure + returns (uint96) + { + uint96 result = uint96(number); + Require.that( + result == number, + FILE, + "Unsafe cast to uint96" + ); + return result; + } + + function to32( + uint256 number + ) + internal + pure + returns (uint32) + { + uint32 result = uint32(number); + Require.that( + result == number, + FILE, + "Unsafe cast to uint32" + ); + return result; + } + + function min( + uint256 a, + uint256 b + ) + internal + pure + returns (uint256) + { + return a < b ? a : b; + } + + function max( + uint256 a, + uint256 b + ) + internal + pure + returns (uint256) + { + return a > b ? a : b; + } +} diff --git a/contracts/lib/dydx/Monetary.sol b/contracts/lib/dydx/Monetary.sol new file mode 100644 index 0000000..a646176 --- /dev/null +++ b/contracts/lib/dydx/Monetary.sol @@ -0,0 +1,44 @@ +/* + + 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. + */ + struct Price { + uint256 value; + } + + /* + * Total value of an some amount of an asset. Equal to (price * amount). + */ + struct Value { + uint256 value; + } +} diff --git a/contracts/lib/dydx/Require.sol b/contracts/lib/dydx/Require.sol new file mode 100644 index 0000000..91fd8f3 --- /dev/null +++ b/contracts/lib/dydx/Require.sol @@ -0,0 +1,7 @@ +pragma solidity ^0.4.0; + +contract Require { + function Require(){ + + } +} diff --git a/contracts/lib/dydx/SafeMath.sol b/contracts/lib/dydx/SafeMath.sol new file mode 100644 index 0000000..dba038f --- /dev/null +++ b/contracts/lib/dydx/SafeMath.sol @@ -0,0 +1,150 @@ +pragma solidity ^0.6.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. + */ + 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. + */ + 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. + */ + function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { + require(b != 0, errorMessage); + return a % b; + } +} diff --git a/contracts/lib/dydx/Time.sol b/contracts/lib/dydx/Time.sol new file mode 100644 index 0000000..a7300ea --- /dev/null +++ b/contracts/lib/dydx/Time.sol @@ -0,0 +1,42 @@ +/* + + 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 { Math } from "./Math.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 Math.to32(block.timestamp); + } +} diff --git a/contracts/lib/dydx/Types.sol b/contracts/lib/dydx/Types.sol new file mode 100644 index 0000000..06b277f --- /dev/null +++ b/contracts/lib/dydx/Types.sol @@ -0,0 +1,287 @@ +/* + + 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 "./SafeMath.sol"; +import { Math } from "./Math.sol"; + + +/** + * @title Types + * @author dYdX + * + * Library for interacting with the basic structs used in Solo + */ +library Types { + using Math for uint256; + + // ============ 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; + } + + // ============ 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; + } +}