Skip to content

Commit

Permalink
Implement a proposed contract structure
Browse files Browse the repository at this point in the history
Implement a proposed contract structure

Update

Update

Add more internal

Add and rework more internal

format

Format

Add more internal

Add more internal

Remove timestamp usage

Move stuff around

Remove unnecessary constants

Address comments

Remove some unused libs and test signed commit
  • Loading branch information
pakim249CAL committed Dec 18, 2022
1 parent 9fee7ab commit 64a999c
Show file tree
Hide file tree
Showing 30 changed files with 1,474 additions and 55 deletions.
9 changes: 9 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,12 @@
[submodule "lib/morpho-data-structures"]
path = lib/morpho-data-structures
url = https://github.com/morpho-dao/morpho-data-structures
[submodule "lib/morpho-utils"]
path = lib/morpho-utils
url = https://github.com/morpho-dao/morpho-utils
[submodule "lib/aave-v3-core"]
path = lib/aave-v3-core
url = https://github.com/aave/aave-v3-core
[submodule "lib/aave-v3-periphery"]
path = lib/aave-v3-periphery
url = https://github.com/aave/aave-v3-periphery
1 change: 1 addition & 0 deletions lib/aave-v3-core
Submodule aave-v3-core added at f3e037
1 change: 1 addition & 0 deletions lib/aave-v3-periphery
Submodule aave-v3-periphery added at 932f36
1 change: 1 addition & 0 deletions lib/morpho-utils
Submodule morpho-utils added at dbf669
13 changes: 9 additions & 4 deletions remappings.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
ds-test/=lib/forge-std/lib/ds-test/src/
forge-std/=lib/forge-std/src/
openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts
morpho-data-structures/=lib/morpho-data-structures/contracts/
@ds-test/=lib/forge-std/lib/ds-test/src/
@forge-std/=lib/forge-std/src/

@morpho-data-structures/=lib/morpho-data-structures/contracts/
@morpho-utils/=lib/morpho-utils/src/

@openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts
@openzeppelin/=node_modules/@openzeppelin/
@aave/core-v3/=lib/aave-v3-core/
@aave/periphery-v3/=lib/aave-v3-periphery/
8 changes: 8 additions & 0 deletions src/EntryPositionsManager.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.17;

import {Types, Events, Errors, MarketLib} from "./libraries/Libraries.sol";

import {MatchingEngine} from "./MatchingEngine.sol";

contract EntryPositionsManager is MatchingEngine {}
8 changes: 8 additions & 0 deletions src/ExitPositionsManager.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.17;

import {Types, Events, Errors, MarketLib} from "./libraries/Libraries.sol";

import {MatchingEngine} from "./MatchingEngine.sol";

contract ExitPositionsManager is MatchingEngine {}
171 changes: 171 additions & 0 deletions src/MatchingEngine.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.17;

import {Types, Events, ThreeHeapOrdering, Math, WadRayMath} from "./libraries/Libraries.sol";

import {MorphoInternal} from "./MorphoInternal.sol";

abstract contract MatchingEngine is MorphoInternal {
using Math for uint256;
using ThreeHeapOrdering for ThreeHeapOrdering.HeapArray;
using WadRayMath for uint256;

function _matchSuppliers(address poolToken, uint256 amount, uint256 maxLoops)
internal
returns (uint256 matched, uint256 loopsDone)
{
Types.Market storage market = _market[poolToken];
return _matchOrUnmatch(
_marketBalances[poolToken].suppliersPool,
_marketBalances[poolToken].suppliersP2P,
Types.MatchVars({
poolToken: poolToken,
poolIndex: market.poolSupplyIndex,
p2pIndex: market.p2pSupplyIndex,
amount: amount,
maxLoops: maxLoops,
borrow: false,
matching: true
})
);
}

function _matchBorrowers(address poolToken, uint256 amount, uint256 maxLoops)
internal
returns (uint256 matched, uint256 loopsDone)
{
Types.Market storage market = _market[poolToken];
return _matchOrUnmatch(
_marketBalances[poolToken].borrowersPool,
_marketBalances[poolToken].borrowersP2P,
Types.MatchVars({
poolToken: poolToken,
poolIndex: market.poolBorrowIndex,
p2pIndex: market.p2pBorrowIndex,
amount: amount,
maxLoops: maxLoops,
borrow: true,
matching: true
})
);
}

function _unmatchSuppliers(address poolToken, uint256 amount, uint256 maxLoops)
internal
returns (uint256 unmatched)
{
Types.Market storage market = _market[poolToken];
(unmatched,) = _matchOrUnmatch(
_marketBalances[poolToken].suppliersPool,
_marketBalances[poolToken].suppliersP2P,
Types.MatchVars({
poolToken: poolToken,
poolIndex: market.poolSupplyIndex,
p2pIndex: market.p2pSupplyIndex,
amount: amount,
maxLoops: maxLoops,
borrow: false,
matching: false
})
);
}

function _unmatchBorrowers(address poolToken, uint256 amount, uint256 maxLoops)
internal
returns (uint256 unmatched)
{
Types.Market storage market = _market[poolToken];
(unmatched,) = _matchOrUnmatch(
_marketBalances[poolToken].borrowersPool,
_marketBalances[poolToken].borrowersP2P,
Types.MatchVars({
poolToken: poolToken,
poolIndex: market.poolBorrowIndex,
p2pIndex: market.p2pBorrowIndex,
amount: amount,
maxLoops: maxLoops,
borrow: true,
matching: false
})
);
}

function _matchOrUnmatch(
ThreeHeapOrdering.HeapArray storage heapOnPool,
ThreeHeapOrdering.HeapArray storage heapInP2P,
Types.MatchVars memory vars
) internal returns (uint256 matched, uint256 loopsDone) {
if (vars.maxLoops == 0) return (0, 0);

uint256 remainingToMatch = vars.amount;

// prettier-ignore
// This function will be used to decide whether to use the algorithm for matching or for unmatching.
function(uint256, uint256, uint256, uint256, uint256)
pure returns (uint256, uint256, uint256) f;
ThreeHeapOrdering.HeapArray storage workingHeap;

if (vars.matching) {
workingHeap = heapOnPool;
f = _matchStep;
} else {
workingHeap = heapInP2P;
f = _unmatchStep;
}

for (; loopsDone < vars.maxLoops; ++loopsDone) {
// Safe unchecked because `gasLeftAtTheBeginning` >= gas left now.
address firstUser = workingHeap.getHead();
if (firstUser == address(0)) break;

uint256 onPool;
uint256 inP2P;

(onPool, inP2P, remainingToMatch) = f(
heapOnPool.getValueOf(firstUser),
heapInP2P.getValueOf(firstUser),
vars.poolIndex,
vars.p2pIndex,
remainingToMatch
);

if (!vars.borrow) {
_updateSupplierInDS(vars.poolToken, firstUser, onPool, inP2P);
} else {
_updateBorrowerInDS(vars.poolToken, firstUser, onPool, inP2P);
}

emit Events.PositionUpdated(vars.borrow, firstUser, vars.poolToken, onPool, inP2P);
}

// Safe unchecked because `gasLeftAtTheBeginning` >= gas left now.
// And _amount >= remainingToMatch.
unchecked {
matched = vars.amount - remainingToMatch;
}
}

function _matchStep(uint256 poolBalance, uint256 p2pBalance, uint256 poolIndex, uint256 p2pIndex, uint256 remaining)
internal
pure
returns (uint256 newPoolBalance, uint256 newP2PBalance, uint256 newRemaining)
{
uint256 toProcess = Math.min(poolBalance.rayMul(poolIndex), remaining);
newRemaining = remaining - toProcess;
newPoolBalance = poolBalance - toProcess.rayDiv(poolIndex);
newP2PBalance = p2pBalance + toProcess.rayDiv(p2pIndex);
}

function _unmatchStep(
uint256 poolBalance,
uint256 p2pBalance,
uint256 poolIndex,
uint256 p2pIndex,
uint256 remaining
) internal pure returns (uint256 newPoolBalance, uint256 newP2PBalance, uint256 newRemaining) {
uint256 toProcess = Math.min(p2pBalance.rayMul(p2pIndex), remaining);
newRemaining = remaining - toProcess;
newPoolBalance = poolBalance + toProcess.rayDiv(poolIndex);
newP2PBalance = p2pBalance - toProcess.rayDiv(p2pIndex);
}
}
43 changes: 8 additions & 35 deletions src/Morpho.sol
Original file line number Diff line number Diff line change
@@ -1,20 +1,17 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.17;

import {ThreeHeapOrdering} from "morpho-data-structures/ThreeHeapOrdering.sol";
import {Events} from "./libraries/Events.sol";
import {Errors} from "./libraries/Errors.sol";
import {Types} from "./libraries/Types.sol";
import {Types, Events, Errors, MarketLib} from "./libraries/Libraries.sol";

import {ERC1155Upgradeable} from "openzeppelin-contracts-upgradeable/token/ERC1155/ERC1155Upgradeable.sol";
import {OwnableUpgradeable} from "openzeppelin-contracts-upgradeable/access/OwnableUpgradeable.sol";
import {MorphoGettersAndSetters} from "./MorphoGettersAndSetters.sol";

contract Morpho is ERC1155Upgradeable, OwnableUpgradeable {
using ThreeHeapOrdering for ThreeHeapOrdering.HeapArray;
// import {IERC1155} from "./interfaces/IERC1155.sol";
// import {OwnableUpgradeable} from "openzeppelin-contracts-upgradeable/access/OwnableUpgradeable.sol";

/// STORAGE ///

mapping(address => Types.Market) internal markets;
// @note: To add: IERC1155, Ownable
contract Morpho is MorphoGettersAndSetters {
using MarketLib for Types.MarketBalances;
using MarketLib for Types.Market;

/// EXTERNAL ///

Expand Down Expand Up @@ -42,28 +39,4 @@ contract Morpho is ERC1155Upgradeable, OwnableUpgradeable {
external
returns (uint256 repaid, uint256 seized)
{}

/// PUBLIC ///

function decodeId(uint256 _id) public pure returns (address underlying, Types.PositionType positionType) {
underlying = address(uint160(_id));
positionType = Types.PositionType(_id & 0xf);
}

/// ERC1155 ///

function balanceOf(address _user, uint256 _id) public view virtual override returns (uint256) {
(address underlying, Types.PositionType positionType) = decodeId(_id);
Types.Market storage market = markets[underlying];

if (positionType == Types.PositionType.COLLATERAL) {
return market.collateralScaledBalance[_user];
} else if (positionType == Types.PositionType.SUPPLY) {
return market.suppliersP2P.getValueOf(_user) + market.suppliersP2P.getValueOf(_user); // TODO: take into account indexes.
} else if (positionType == Types.PositionType.BORROW) {
return market.borrowersP2P.getValueOf(_user) + market.borrowersP2P.getValueOf(_user); // TODO: take into account indexes.
} else {
return 0;
}
}
}
72 changes: 72 additions & 0 deletions src/MorphoGettersAndSetters.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.17;

import {Types, Events, Errors, MarketLib} from "./libraries/Libraries.sol";

import {MorphoInternal} from "./MorphoInternal.sol";

abstract contract MorphoGettersAndSetters is MorphoInternal {
using MarketLib for Types.MarketBalances;
using MarketLib for Types.Market;

/// STORAGE ///

function market(address poolToken) external view returns (Types.Market memory) {
return _market[poolToken];
}

function scaledPoolSupplyBalance(address poolToken, address user) external view returns (uint256) {
return _marketBalances[poolToken].scaledPoolSupplyBalance(user);
}

function scaledP2PSupplyBalance(address poolToken, address user) external view returns (uint256) {
return _marketBalances[poolToken].scaledP2PSupplyBalance(user);
}

function scaledPoolBorrowBalance(address poolToken, address user) external view returns (uint256) {
return _marketBalances[poolToken].scaledPoolBorrowBalance(user);
}

function scaledP2PBorrowBalance(address poolToken, address user) external view returns (uint256) {
return _marketBalances[poolToken].scaledP2PBorrowBalance(user);
}

function scaledCollateralBalance(address poolToken, address user) external view returns (uint256) {
return _marketBalances[poolToken].scaledCollateralBalance(user);
}

function userMarkets(address user) external view returns (Types.UserMarkets memory) {
return _userMarkets[user];
}

function maxSortedUsers() external view returns (uint256) {
return _maxSortedUsers;
}

function isClaimRewardsPaused() external view returns (bool) {
return _isClaimRewardsPaused;
}

/// UTILITY ///

function decodeId(uint256 id) external pure returns (address poolToken, Types.PositionType positionType) {
return _decodeId(id);
}

/// ERC1155 ///

function balanceOf(address user, uint256 id) external view returns (uint256) {
(address poolToken, Types.PositionType positionType) = _decodeId(id);
Types.MarketBalances storage marketBalances = _marketBalances[poolToken];

if (positionType == Types.PositionType.COLLATERAL) {
return marketBalances.scaledCollateralBalance(user);
} else if (positionType == Types.PositionType.SUPPLY) {
return marketBalances.scaledP2PSupplyBalance(user) + marketBalances.scaledPoolSupplyBalance(user); // TODO: take into account indexes.
} else if (positionType == Types.PositionType.BORROW) {
return marketBalances.scaledP2PBorrowBalance(user) + marketBalances.scaledPoolBorrowBalance(user); // TODO: take into account indexes.
} else {
return 0;
}
}
}
Loading

0 comments on commit 64a999c

Please sign in to comment.