Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 32 additions & 66 deletions contracts/CapitalAgent.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pragma solidity =0.8.23;

import "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "@openzeppelin/contracts/utils/Strings.sol";
import "./interfaces/ISalesPolicy.sol";
import "./interfaces/IExchangeAgent.sol";
import "./interfaces/ICapitalAgent.sol";
Expand All @@ -15,7 +16,7 @@ import "./interfaces/ICapitalAgent.sol";
**/
contract CapitalAgent is ICapitalAgent, ReentrancyGuardUpgradeable, AccessControlUpgradeable {
bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE");

using Strings for uint256;
address public exchangeAgent;
address public salesPolicyFactory;
address public usdcToken;
Expand All @@ -25,7 +26,6 @@ contract CapitalAgent is ICapitalAgent, ReentrancyGuardUpgradeable, AccessContro
uint256 totalCapital;
address currency;
bool exist;
uint256 totalWithdrawPendingCapital;
}

struct PolicyInfo {
Expand All @@ -43,11 +43,20 @@ contract CapitalAgent is ICapitalAgent, ReentrancyGuardUpgradeable, AccessContro

PolicyInfo public policyInfo;

uint256 public totalUtilizedAmount;

// Maximum Leverage Ratio
uint256 public MLR;

/*
Value in %. 80 means 80% of the total MLR value
Currently we use the MLR to allow users to buy coverages.
The idea with the new approach is to add a safety margin on top of
the MLR so that we can only sell coverages for about 80% of the MLR.
That means that if the MLR is 200% and the pool has 100k tokens, we could
only sell 160k in coverage. At the same time, if 160k are sold in coverages,
we need to make sure that the pools always have at least 80k tokens.
*/
uint256 public MLR_THRESHOLD = 80;

uint256 public constant CALC_PRECISION = 1e18;

mapping(address => bool) public poolWhiteList;
Expand All @@ -63,8 +72,7 @@ contract CapitalAgent is ICapitalAgent, ReentrancyGuardUpgradeable, AccessContro
event LogUpdatePolicyCoverage(
address indexed _policy,
uint256 _amount,
uint256 _policyUtilized,
uint256 _totalUtilizedAmount
uint256 _policyUtilized
);
event LogUpdatePolicyExpired(address indexed _policy, uint256 _policyTokenId);
event LogMarkToClaimPolicy(address indexed _policy, uint256 _policyTokenId);
Expand All @@ -75,7 +83,6 @@ contract CapitalAgent is ICapitalAgent, ReentrancyGuardUpgradeable, AccessContro
event LogRemovePoolWhiteList(address indexed _pool);
event LogSetOperator(address indexed _operator);
event LogSetUSDC(address indexed _usdcToken);
event LogupdatePoolWithdrawPendingCapital(address indexed _ssip, uint256 _poolPendingCapital);

function initialize(
address _exchangeAgent,
Expand Down Expand Up @@ -110,9 +117,9 @@ contract CapitalAgent is ICapitalAgent, ReentrancyGuardUpgradeable, AccessContro
return (_policy.policy, _policy.utilizedAmount, _policy.exist);
}

function getPoolInfo(address _pool) external view returns (uint256, address, bool, uint256) {
function getPoolInfo(address _pool) external view returns (uint256, address, bool) {
PoolInfo memory _poolInfo = poolInfo[_pool];
return (_poolInfo.totalCapital, _poolInfo.currency, _poolInfo.exist, _poolInfo.totalWithdrawPendingCapital);
return (_poolInfo.totalCapital, _poolInfo.currency, _poolInfo.exist);
}

// Getter function to return the poolList array
Expand Down Expand Up @@ -180,7 +187,7 @@ contract CapitalAgent is ICapitalAgent, ReentrancyGuardUpgradeable, AccessContro
* @dev return total capital in usdc staked in capital agent by pools
**/
function totalCapitalStaked() public view returns (uint256) {
return _getTotalCapitalStakedInUSDC();
return _getTotalCapitalAvailableInUSDC();
}

/**
Expand Down Expand Up @@ -220,7 +227,7 @@ contract CapitalAgent is ICapitalAgent, ReentrancyGuardUpgradeable, AccessContro
}

poolList.push(_ssip);
poolInfo[_ssip] = PoolInfo({totalCapital: 0, currency: _currency, exist: true, totalWithdrawPendingCapital: 0});
poolInfo[_ssip] = PoolInfo({totalCapital: 0, currency: _currency, exist: true});

emit LogAddPool(_ssip, _currency);
}
Expand All @@ -238,7 +245,7 @@ contract CapitalAgent is ICapitalAgent, ReentrancyGuardUpgradeable, AccessContro
existedCurrencies[_currency] = true;
currencyList.push(_currency);
}
poolInfo[_ssip] = PoolInfo({totalCapital: 0, currency: _currency, exist: true, totalWithdrawPendingCapital: 0});
poolInfo[_ssip] = PoolInfo({totalCapital: 0, currency: _currency, exist: true});
poolList.push(_ssip);
emit LogAddPool(_ssip, _currency);
}
Expand Down Expand Up @@ -292,7 +299,6 @@ contract CapitalAgent is ICapitalAgent, ReentrancyGuardUpgradeable, AccessContro
**/
function removePolicy() external onlyRole(ADMIN_ROLE) nonReentrant {
require(policyInfo.exist, "UnoRe: non existing policy on Capital Agent");
totalUtilizedAmount = 0;
address _policy = policyInfo.policy;
policyInfo.policy = address(0);
policyInfo.exist = false;
Expand All @@ -308,9 +314,8 @@ contract CapitalAgent is ICapitalAgent, ReentrancyGuardUpgradeable, AccessContro
**/
function SSIPWithdraw(uint256 _withdrawAmount) external override nonReentrant {
require(poolInfo[msg.sender].exist, "UnoRe: no exist ssip");
require(_checkCapitalByMLR(msg.sender, _withdrawAmount), "UnoRe: minimum capital underflow");
_checkCapitalByMLR(msg.sender, _withdrawAmount);
_updatePoolCapital(msg.sender, _withdrawAmount, false);
_updatePoolWithdrawPendingCapital(msg.sender, _withdrawAmount, false);
}

/**
Expand Down Expand Up @@ -351,11 +356,6 @@ contract CapitalAgent is ICapitalAgent, ReentrancyGuardUpgradeable, AccessContro
_updatePoolCapital(msg.sender, _stakingAmount, true);
}

function updatePoolWithdrawPendingCapital(address _pool, uint256 _amount, bool isAdd) external nonReentrant {
require(poolInfo[msg.sender].exist, "UnoRe: no exist ssip");
_updatePoolWithdrawPendingCapital(_pool, _amount, isAdd);
}

/**
* @dev return if pool can withdraw this amount,
* remaning pool capital times MLR should be greater than or equal to the totalPremiumSold
Expand Down Expand Up @@ -430,41 +430,27 @@ contract CapitalAgent is ICapitalAgent, ReentrancyGuardUpgradeable, AccessContro
emit LogUpdatePoolCapital(_pool, poolInfo[_pool].totalCapital, totalCapitalStakedByCurrency[currency]);
}

function _updatePoolWithdrawPendingCapital(address _pool, uint256 _amount, bool isAdd) private {
if (!isAdd) {
require(poolInfo[_pool].totalWithdrawPendingCapital >= _amount, "UnoRe: total Capital Pending underflow");
}
poolInfo[_pool].totalWithdrawPendingCapital = isAdd
? poolInfo[_pool].totalWithdrawPendingCapital + _amount
: poolInfo[_pool].totalWithdrawPendingCapital - _amount;
emit LogupdatePoolWithdrawPendingCapital(_pool, poolInfo[_pool].totalWithdrawPendingCapital);
}

function _updatePolicyCoverage(uint256 _amount, bool isAdd) private {
if (!isAdd) {
require(policyInfo.utilizedAmount >= _amount, "UnoRe: policy coverage overflow");
}
policyInfo.utilizedAmount = isAdd ? policyInfo.utilizedAmount + _amount : policyInfo.utilizedAmount - _amount;
totalUtilizedAmount = isAdd ? totalUtilizedAmount + _amount : totalUtilizedAmount - _amount;
emit LogUpdatePolicyCoverage(policyInfo.policy, _amount, policyInfo.utilizedAmount, totalUtilizedAmount);
emit LogUpdatePolicyCoverage(policyInfo.policy, _amount, policyInfo.utilizedAmount);
}

function _checkCapitalByMLR(address _pool, uint256 _withdrawAmount) private view returns (bool) {
address currency = poolInfo[_pool].currency;
uint256 totalCapitalStakedInUSDC;
uint256 totalWithdrawPendingCapital;

uint256 totalCapitalAvailableInUSDC;
//Withdraw amount in USDC
uint256 withdrawInUSDC = _convertTokenToUSDC(currency, _withdrawAmount);
//Total Capital Staked in Pools
totalCapitalStakedInUSDC = _convertTokenToUSDC(currency, _getTotalCapitalStakedInUSDC());
//Total Capital pending in withdraw
totalWithdrawPendingCapital = _getTotalPendingCapitalInUSDC();
uint256 totalInPoolInUSDC = totalCapitalStakedInUSDC - totalWithdrawPendingCapital;
totalCapitalAvailableInUSDC = _convertTokenToUSDC(currency, _getTotalCapitalAvailableInUSDC());

uint256 maxWithdrawAmountInUSDC = (((totalCapitalAvailableInUSDC) * MLR) / CALC_PRECISION) - policyInfo.utilizedAmount;

bool isMLRPass = totalUtilizedAmount <= ((totalInPoolInUSDC - withdrawInUSDC) * MLR) / CALC_PRECISION;
require(maxWithdrawAmountInUSDC >= withdrawInUSDC, string.concat("UnoRe: max ammount is", maxWithdrawAmountInUSDC.toString()));

return isMLRPass;
return policyInfo.utilizedAmount <= ((totalCapitalAvailableInUSDC - withdrawInUSDC) * MLR) / CALC_PRECISION;
}

function _convertTokenToUSDC(address _currency, uint256 _amount) private view returns (uint256) {
Expand All @@ -480,7 +466,7 @@ contract CapitalAgent is ICapitalAgent, ReentrancyGuardUpgradeable, AccessContro
return tokenInUSDC;
}

function _getTotalCapitalStakedInUSDC() private view returns (uint256) {
function _getTotalCapitalAvailableInUSDC() private view returns (uint256) {
uint256 totalCapitalStakedInUSDC;
for (uint256 i = 0; i < currencyList.length; i++) {
address currency = currencyList[i];
Expand All @@ -491,32 +477,12 @@ contract CapitalAgent is ICapitalAgent, ReentrancyGuardUpgradeable, AccessContro
return totalCapitalStakedInUSDC;
}

function getTotalPendingCapitalInUSDC() public view returns (uint256) {
return _getTotalPendingCapitalInUSDC();
}

/**
* @dev returns total value in "pending" state from all pools
**/
function _getTotalPendingCapitalInUSDC() private view returns (uint256) {
uint256 totalPendingCapitalInUSDC;
for (uint256 i = 0; i < poolList.length; i++) {
if (poolInfo[poolList[i]].exist == true) {
totalPendingCapitalInUSDC =
totalPendingCapitalInUSDC +
_convertTokenToUSDC(poolInfo[poolList[i]].currency, poolInfo[poolList[i]].totalWithdrawPendingCapital);
}
}
return totalPendingCapitalInUSDC;
}

function _checkCoverageByMLR(uint256 _newCoverageAmount) private view returns (bool) {
uint256 totalCapitalStakedInUSDC = _getTotalCapitalStakedInUSDC();
uint256 totalCapitalPendingInUSDC = _getTotalPendingCapitalInUSDC();
uint256 scaledMLR = MLR / CALC_PRECISION;
uint256 totalCapitalStakedInUSDC = _getTotalCapitalAvailableInUSDC();
uint256 availableCoverage = totalCapitalStakedInUSDC * MLR / CALC_PRECISION;

return
totalUtilizedAmount + _newCoverageAmount <=
((totalCapitalStakedInUSDC - totalCapitalPendingInUSDC) * scaledMLR);
policyInfo.utilizedAmount + _newCoverageAmount <= (availableCoverage * MLR_THRESHOLD / 100);
}

/**
Expand Down
55 changes: 0 additions & 55 deletions contracts/PremiumPool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,6 @@ contract PremiumPool is IPremiumPool, ReentrancyGuard, AccessControl, Pausable {
event PremiumWithdraw(address indexed _currency, address indexed _to, uint256 _amount);
event LogBuyBackAndBurn(address indexed _operator, address indexed _premiumPool, uint256 _unoAmount);
event LogCollectPremium(address indexed _from, address _premiumCurrency, uint256 _premiumAmount);
event LogDepositToSyntheticSSRPRewarder(address indexed _rewarder, uint256 _amountDeposited);
event LogDepositToSyntheticSSIPRewarder(address indexed _rewarder, address indexed _currency, uint256 _amountDeposited);
event LogAddCurrency(address indexed _premiumPool, address indexed _currency);
event LogRemoveCurrency(address indexed _premiumPool, address indexed _currency);
event LogMaxApproveCurrency(address indexed _premiumPool, address indexed _currency, address indexed _to);
Expand Down Expand Up @@ -133,59 +131,6 @@ contract PremiumPool is IPremiumPool, ReentrancyGuard, AccessControl, Pausable {
emit LogCollectPremium(msg.sender, _premiumCurrency, _premiumAmount);
}

function depositToSyntheticSSRPRewarder(address _rewarder) external onlyRole(ADMIN_ROLE) whenNotPaused nonReentrant {
require(_rewarder != address(0), "UnoRe: zero address");
enforceHasContractCode(_rewarder, "UnoRe: no contract address");
uint256 usdcAmountToDeposit = 0;
if (ssrpPremiumEth > 0) {
TransferHelper.safeTransferETH(exchangeAgent, ssrpPremiumEth);
uint256 convertedAmount = IExchangeAgent(exchangeAgent).convertForToken(address(0), usdcToken, ssrpPremiumEth);
usdcAmountToDeposit += convertedAmount;
ssrpPremiumEth = 0;
}
for (uint256 ii = 0; ii < availableCurrencyList.length; ii++) {
if (ssrpPremium[availableCurrencyList[ii]] > 0) {
if (availableCurrencyList[ii] == usdcToken) {
usdcAmountToDeposit += ssrpPremium[availableCurrencyList[ii]];
} else {
uint256 convertedUSDCAmount = IExchangeAgent(exchangeAgent).convertForToken(
availableCurrencyList[ii],
usdcToken,
ssrpPremium[availableCurrencyList[ii]]
);
usdcAmountToDeposit += convertedUSDCAmount;
}
ssrpPremium[availableCurrencyList[ii]] = 0;
}
}
if (usdcAmountToDeposit > 0) {
TransferHelper.safeTransfer(usdcToken, _rewarder, usdcAmountToDeposit);
emit LogDepositToSyntheticSSRPRewarder(_rewarder, usdcAmountToDeposit);
}
}

function depositToSyntheticSSIPRewarder(
address _currency,
address _rewarder,
uint256 _amount
) external onlyRole(ADMIN_ROLE) whenNotPaused nonReentrant {
require(_rewarder != address(0), "UnoRe: zero address");
enforceHasContractCode(_rewarder, "UnoRe: no contract address");
if (_currency == address(0) && ssipPremiumEth > 0) {
require(_amount <= ssipPremiumEth, "UnoRe: premium balance overflow");
TransferHelper.safeTransferETH(_rewarder, _amount);
ssipPremiumEth -= _amount;
emit LogDepositToSyntheticSSIPRewarder(_rewarder, _currency, _amount);
} else {
if (availableCurrencies[_currency] && ssipPremium[_currency] > 0) {
require(_amount <= ssipPremium[_currency], "UnoRe: premium balance overflow");
TransferHelper.safeTransfer(_currency, _rewarder, _amount);
ssipPremium[_currency] -= _amount;
emit LogDepositToSyntheticSSIPRewarder(_rewarder, _currency, _amount);
}
}
}

function buyBackAndBurn() external onlyRole(ADMIN_ROLE) isAlive whenNotPaused {
uint256 unoAmount = 0;
if (backBurnPremiumEth > 0) {
Expand Down
Loading