Skip to content

Commit

Permalink
Merge pull request #2 from MMtis/main
Browse files Browse the repository at this point in the history
V1.0
  • Loading branch information
Maroutis authored Jun 4, 2023
2 parents 191a353 + db6c9d9 commit 0b1af83
Show file tree
Hide file tree
Showing 18 changed files with 1,404 additions and 494 deletions.
9 changes: 0 additions & 9 deletions .env-example

This file was deleted.

6 changes: 6 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,9 @@
[submodule "lib/v3-core"]
path = lib/v3-core
url = https://github.com/Uniswap/v3-core
[submodule "lib/v2-periphery"]
path = lib/v2-periphery
url = https://github.com/Uniswap/v2-periphery
[submodule "lib/v2-core"]
path = lib/v2-core
url = https://github.com/Uniswap/v2-core
1 change: 1 addition & 0 deletions foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ libs = ['lib']
goerli = "${GOERLI_RPC_URL}"
sepolia = "${SEPOLIA_RPC_URL}"
polygon = "${MUMBAI_RPC_URL}"
ETH_RPC_URL = "${ETH_RPC_URL}"

[etherscan]
goerli = { key = "${ETHERSCAN_API_KEY}" }
Expand Down
1 change: 1 addition & 0 deletions lib/v2-core
Submodule v2-core added at 4dd590
1 change: 1 addition & 0 deletions lib/v2-periphery
Submodule v2-periphery added at 0335e8
125 changes: 97 additions & 28 deletions src/BasketHandler.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,37 +7,78 @@ import "./PriceConverter.sol";

struct Basket {
IERC20[] erc20s; // enumerated keys for refAmts
mapping(IERC20 => uint256) tokenAmts; // Amount of tokens in decimals
mapping(IERC20 => uint8) decimals;
mapping(IERC20 => uint256) weightsInPercent; // {ref/BU}
mapping(IERC20 => AggregatorV3Interface) priceFeedBasket;
bool empty;
AggregatorV3Interface priceFeedEth;
bool empty; // The struct is not imported if the bool is not added (solidity bug ?)
}

uint256 constant FIX_ZERO = 0;

/*
* @title BasketLibP0
/**
* @title BasketLibrary
* @author
* @notice This is a library that manages and implements helpful function for Basket structs
* @dev This functions will be used in the vault contract
*/
library BasketLib {
uint256 constant FIX_ZERO = 0;
using PriceConverter for uint256;

function empty(Basket storage self, IERC20 token) internal {
delete self.tokenAmts[token];
delete self.weightsInPercent[token];
delete self.priceFeedBasket[token];

uint256 length = self.erc20s.length;
for (uint i = 0; i < length; i++) {
if (self.erc20s[i] == token) {
self.erc20s[i] = self.erc20s[length - 1];
self.erc20s.pop();
break;
}
}
}

/// Set self to a fresh, empty basket
// self'.erc20s = [] (empty list)
// self'.refAmts = {} (empty map)
function empty(Basket storage self) internal {
uint256 length = self.erc20s.length;
for (uint256 i = 0; i < length; ++i) {
delete self.tokenAmts[self.erc20s[i]];
delete self.weightsInPercent[self.erc20s[i]];
delete self.priceFeedBasket[self.erc20s[i]];
}
delete self.erc20s;
}

function setFrom(
Basket storage self,
address[] memory erc20s,
uint256[] memory tokenAmts,
uint8[] memory decimals,
uint256[] memory weightsInPercent,
AggregatorV3Interface[] memory priceFeedBasket
) internal {
empty(self);
uint256 length = erc20s.length;
for (uint256 i = 0; i < length; ++i) {
self.erc20s.push(IERC20(erc20s[i]));
self.tokenAmts[IERC20(erc20s[i])] = tokenAmts[i];
self.decimals[IERC20(erc20s[i])] = decimals[i];
self.weightsInPercent[IERC20(erc20s[i])] = weightsInPercent[i];
self.priceFeedBasket[IERC20(erc20s[i])] = priceFeedBasket[i];
}
}

/// Set `self` equal to `other`
function setFrom(Basket storage self, Basket storage other) internal {
empty(self);
uint256 length = other.erc20s.length;
for (uint256 i = 0; i < length; ++i) {
self.erc20s.push(other.erc20s[i]);
self.tokenAmts[other.erc20s[i]] = other.tokenAmts[other.erc20s[i]];
self.weightsInPercent[other.erc20s[i]] = other.weightsInPercent[
other.erc20s[i]
];
Expand All @@ -47,49 +88,77 @@ library BasketLib {
}
}

function updateWeights(Basket storage self) internal {
uint256 length = self.erc20s.length;
for (uint256 i = 0; i < length; ++i) {
IERC20 _tok = self.erc20s[i];
self.weightsInPercent[_tok] = ((getSingleBalance(self, _tok) *
100) / getBasketBalance(self));
}
}

/// Add `weight` to the refAmount of collateral token `tok` in the basket `self`
// self'.refAmts[tok] = self.refAmts[tok] + weight
// self'.erc20s is keys(self'.refAmts)
function add(
Basket storage self,
IERC20 tok,
uint256 _weight,
IERC20 _tok,
uint256 _amount,
uint8 _decimal,
AggregatorV3Interface _priceFeed
) internal {
if (_weight == FIX_ZERO) return;
if (self.weightsInPercent[tok] == FIX_ZERO) {
self.erc20s.push(tok);
self.weightsInPercent[tok] = _weight;
self.priceFeedBasket[tok] = _priceFeed;
if (_amount == FIX_ZERO) return;
if (self.tokenAmts[_tok] == FIX_ZERO) {
self.erc20s.push(_tok);
self.tokenAmts[_tok] = _amount;
self.decimals[_tok] = _decimal;
self.priceFeedBasket[_tok] = _priceFeed;
} else {
self.weightsInPercent[tok] += _weight;
self.tokenAmts[_tok] += _amount;
}
updateWeights(self);
}

function Balance(
function reduce(
Basket storage self,
address account
) internal view returns (uint256 balance) {
IERC20 _tok,
uint256 _amount
) internal {
if (_amount == FIX_ZERO) return;
if (self.tokenAmts[_tok] == _amount) {
empty(self, _tok);
} else {
self.tokenAmts[_tok] -= _amount;
}
updateWeights(self);
}

function Transfer(Basket storage self, address sender) internal {
require(self.erc20s.length > 0, "Basket is empty");
uint256 length = self.erc20s.length;
for (uint256 i = 0; i < length; ++i) {
balance += self.erc20s[i].balanceOf(account).getConversionRate(
self.priceFeedBasket[self.erc20s[i]]
);
self.erc20s[i].transfer(sender, self.tokenAmts[self.erc20s[i]]);
}
empty(self);
}

function Transfer(
function getSingleBalance(
Basket storage self,
address sender,
address positionAddress
) internal {
require(self.erc20s.length > 0, "Basket is empty");
IERC20 token
) internal view returns (uint256 balance) {
balance = self.tokenAmts[token].getConversionRate(
self.priceFeedBasket[token],
self.decimals[token],
self.priceFeedEth
);
}

function getBasketBalance(
Basket storage self
) internal view returns (uint256 balance) {
uint256 length = self.erc20s.length;
for (uint256 i = 0; i < length; ++i) {
self.erc20s[i].transfer(
sender,
self.erc20s[i].balanceOf(positionAddress)
);
balance += getSingleBalance(self, self.erc20s[i]);
}
}
}
16 changes: 14 additions & 2 deletions src/Coin.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,19 @@ import "./Notary.sol";
contract Coin is ERC20 {
Notary notary;

modifier onlyAuthorized() {
require(notary.isValidPosition(msg.sender), "Caller is not authorized");
_;
}

constructor(address _notaryAddress) ERC20("Coin", "coin") {
notary = Notary(_notaryAddress);
}

function decimals() public view override returns (uint8) {
return 18;
}

/**
* @dev Mints for authenticated position contracts.
*/
Expand All @@ -31,15 +40,18 @@ contract Coin is ERC20 {
address _positionAddress,
address _receiver,
uint256 _moreDebt
) external {
) external onlyAuthorized {
// require(
// notary.isValidPosition(_positionAddress),
// "Caller is not authorized to mint"
// );
_mint(_receiver, _moreDebt);
}

function burn(address owner, uint256 _stablecoinAmount) external {
function burn(
address owner,
uint256 _stablecoinAmount
) external onlyAuthorized {
require(_stablecoinAmount > 0, "Invalid stablecoin amount");

_burn(owner, _stablecoinAmount);
Expand Down
50 changes: 32 additions & 18 deletions src/Notary.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import "lib/openzeppelin-contracts/contracts/utils/math/SafeMath.sol";
import "lib/chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";
import "./BasketHandler.sol";
import "./PriceConverter.sol";
import "./Position.sol";
import "./Vault.sol";

/**
* @dev Notary contract registers and authenticates Positions.
Expand All @@ -17,11 +17,14 @@ import "./Position.sol";
*/
contract Notary is Ownable {
mapping(address => bool) public isValidPosition;
Vault[] public vaults;
uint256 vaultID;

event PositionOpened(address positionAddress);
event VaultOpened(address vaultAddress);

uint256 public immutable RATIO;
address public coinAddress;
address public portfolioAddress;

bool public activated;

Expand All @@ -38,33 +41,44 @@ contract Notary is Ownable {
* @dev Activates the notary by providing the address of a token contract
* that has been configured to reference this address.
*/
function activate(address _coinAddress) public onlyOwner {
function activate(
address _coinAddress,
address _portfolio
) public onlyOwner {
// @Todo check for notary address, investigate recursive implications.
coinAddress = _coinAddress;
portfolioAddress = _portfolio;
activated = true;
}

/**
* @dev Opens a position for a specified vault owner address.
*/
function openPosition(
IERC20[] memory tokens,
uint256[] memory weights,
AggregatorV3Interface[] memory priceFeeds,
address ownerAddress
) public isActivated returns (address positionAddress) {
Position position = new Position(
tokens,
weights,
priceFeeds,
function openVault(
address user,
address ethusd
) public isActivated returns (address vaultAddress) {
Vault vault = new Vault(
coinAddress,
ownerAddress
user,
address(this),
portfolioAddress,
ethusd
);
address _positionAddress = address(position);
address _vaultAddress = address(vault);

isValidPosition[_positionAddress] = true;
isValidPosition[_vaultAddress] = true;
vaults.push(vault);
vaultID += 1;

emit PositionOpened(_positionAddress);
return _positionAddress;
emit VaultOpened(_vaultAddress);
return _vaultAddress;
}

function liquidateVaults() public onlyOwner {
uint256 length = vaults.length;
for (uint256 i = 0; i < length; i++) {
vaults[i].liquidate();
}
}
}
Loading

0 comments on commit 0b1af83

Please sign in to comment.