Skip to content

Commit

Permalink
Feat/proof of reserves (#114)
Browse files Browse the repository at this point in the history
* feat: PoR in _checkMint function
  • Loading branch information
scolear authored Oct 14, 2024
1 parent 65282b7 commit 83af649
Show file tree
Hide file tree
Showing 5 changed files with 358 additions and 34 deletions.
81 changes: 79 additions & 2 deletions contracts/DLCManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "./DLCLinkLibrary.sol";
import "./DLCBTC.sol";

import "@chainlink/contracts/src/v0.8/shared/interfaces/AggregatorV3Interface.sol";

/**
* @author DLC.Link 2024
* @title DLCManager
Expand Down Expand Up @@ -64,7 +66,9 @@ contract DLCManager is

mapping(address => bytes32[]) public userVaults;
mapping(address => bool) private _whitelistedAddresses;
uint256[41] __gap;
bool public porEnabled;
AggregatorV3Interface public dlcBTCPoRFeed;
uint256[40] __gap;

////////////////////////////////////////////////////////////////
// ERRORS //
Expand Down Expand Up @@ -99,6 +103,7 @@ contract DLCManager is
error InsufficientMintedBalance(uint256 minted, uint256 amount);
error FeeRateOutOfBounds(uint256 feeRate);
error UnderCollateralized(uint256 newValueLocked, uint256 valueMinted);
error NotEnoughReserves(uint256 reserves, uint256 amount);

////////////////////////////////////////////////////////////////
// MODIFIERS //
Expand Down Expand Up @@ -147,6 +152,7 @@ contract DLCManager is
btcMintFeeRate = 12; // 0.12% BTC fee for now
btcRedeemFeeRate = 15; // 0.15% BTC fee for now
btcFeeRecipient = btcFeeRecipientToSet;
porEnabled = false;
}

/// @custom:oz-upgrades-unsafe-allow constructor
Expand Down Expand Up @@ -191,6 +197,8 @@ contract DLCManager is
event SetBtcFeeRecipient(string btcFeeRecipient);
event SetWhitelistingEnabled(bool isWhitelistingEnabled);
event TransferTokenContractOwnership(address newOwner);
event SetPorEnabled(bool enabled);
event SetDlcBTCPoRFeed(AggregatorV3Interface feed);

////////////////////////////////////////////////////////////////
// INTERNAL FUNCTIONS //
Expand Down Expand Up @@ -274,6 +282,52 @@ contract DLCManager is
}
}

/**
* @notice Checks mint eligibility.
* @dev Checks if the amount is non-zero.
* @dev If PoR is disabled, returns true.
* @dev If PoR is enabled, checks if the new total value minted is within bounds.
* @dev If the PoR check fails, reverts with an error.
* @param amount dlcBTC to mint.
* @param currentTotalMinted total minted value in all vaults on this chain.
* @return bool whether a call to _mint should happen.
*/
function _checkMint(
uint256 amount,
uint256 currentTotalMinted
) internal view returns (bool) {
if (amount == 0) {
return false;
}

uint256 proposedTotalValueMinted = currentTotalMinted + amount;
return _checkPoR(proposedTotalValueMinted);
}

/**
* @notice Checks Proof of Reserves (PoR) eligibility.
* @dev If PoR is disabled, returns true.
* @dev If PoR is enabled, checks if the proposed total value minted is within bounds.
* @dev If the PoR check fails, reverts with an error.
* @param proposedTotalValueMinted proposed total minted value in all vaults on this chain.
* @return bool whether the proposed total value minted is within bounds.
*/
function _checkPoR(
uint256 proposedTotalValueMinted
) internal view returns (bool) {
if (!porEnabled) {
return true;
}

(, int256 porValue, , , ) = dlcBTCPoRFeed.latestRoundData();
uint256 porValueUint = uint256(porValue);

if (porValueUint < proposedTotalValueMinted) {
revert NotEnoughReserves(porValueUint, proposedTotalValueMinted);
}
return true;
}

function _mintTokens(address to, uint256 amount) internal {
dlcBTC.mint(to, amount);
emit Mint(to, amount);
Expand Down Expand Up @@ -368,14 +422,19 @@ contract DLCManager is
revert DepositTooSmall(amountToLockDiff, minimumDeposit);
}

// We fetch the current total minted value in all vaults before we update this Vault
uint256 currentTotalMinted = getTotalValueMintedInVaults();

dlc.fundingTxId = btcTxId;
dlc.wdTxId = "";
dlc.status = DLCLink.DLCStatus.FUNDED;

dlc.valueLocked = newValueLocked;
dlc.valueMinted = newValueLocked;

_mintTokens(dlc.creator, amountToMint);
if (_checkMint(amountToMint, currentTotalMinted)) {
_mintTokens(dlc.creator, amountToMint);
}

emit SetStatusFunded(
uuid,
Expand Down Expand Up @@ -518,6 +577,14 @@ contract DLCManager is
return vaults;
}

function getTotalValueMintedInVaults() public view returns (uint256) {
uint256 totalValueMinted = 0;
for (uint256 i = 0; i < _index; i++) {
totalValueMinted += dlcs[i].valueMinted;
}
return totalValueMinted;
}

function isWhitelisted(address account) external view returns (bool) {
return _whitelistedAddresses[account];
}
Expand Down Expand Up @@ -670,4 +737,14 @@ contract DLCManager is
function setBurnerOnTokenContract(address burner) external onlyAdmin {
dlcBTC.setBurner(burner);
}

function setPorEnabled(bool enabled) external onlyAdmin {
porEnabled = enabled;
emit SetPorEnabled(enabled);
}

function setDlcBTCPoRFeed(AggregatorV3Interface feed) external onlyAdmin {
dlcBTCPoRFeed = feed;
emit SetDlcBTCPoRFeed(feed);
}
}
14 changes: 7 additions & 7 deletions contracts/mocks/MockV3Aggregator.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;
pragma solidity ^0.8.0;

import "@chainlink/contracts/src/v0.8/shared/interfaces/AggregatorV2V3Interface.sol";

Expand All @@ -21,7 +21,7 @@ contract MockV3Aggregator is AggregatorV2V3Interface {

mapping(uint256 => int256) public override getAnswer;
mapping(uint256 => uint256) public override getTimestamp;
mapping(uint256 => uint256) private _getStartedAt;
mapping(uint256 => uint256) private getStartedAt;

constructor(uint8 _decimals, int256 _initialAnswer) {
decimals = _decimals;
Expand All @@ -34,7 +34,7 @@ contract MockV3Aggregator is AggregatorV2V3Interface {
latestRound++;
getAnswer[latestRound] = _answer;
getTimestamp[latestRound] = block.timestamp;
_getStartedAt[latestRound] = block.timestamp;
getStartedAt[latestRound] = block.timestamp;
}

function updateRoundData(
Expand All @@ -48,7 +48,7 @@ contract MockV3Aggregator is AggregatorV2V3Interface {
latestTimestamp = _timestamp;
getAnswer[latestRound] = _answer;
getTimestamp[latestRound] = _timestamp;
_getStartedAt[latestRound] = _startedAt;
getStartedAt[latestRound] = _startedAt;
}

function getRoundData(
Expand All @@ -68,7 +68,7 @@ contract MockV3Aggregator is AggregatorV2V3Interface {
return (
_roundId,
getAnswer[_roundId],
_getStartedAt[_roundId],
getStartedAt[_roundId],
getTimestamp[_roundId],
_roundId
);
Expand All @@ -89,13 +89,13 @@ contract MockV3Aggregator is AggregatorV2V3Interface {
return (
uint80(latestRound),
getAnswer[latestRound],
_getStartedAt[latestRound],
getStartedAt[latestRound],
getTimestamp[latestRound],
uint80(latestRound)
);
}

function description() external pure override returns (string memory) {
return "v0.6/tests/MockV3Aggregator.sol";
return "v0.8/tests/MockV3Aggregator.sol";
}
}
31 changes: 31 additions & 0 deletions scripts/helpers/chainlink-por-addresses.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
const { ethers } = require('hardhat');

async function deployAndGetMockAggregatorAddress() {
console.log('Deploying mock aggregator...');
const MockAggregator = await ethers.getContractFactory('MockV3Aggregator');
const mockAggregator = await MockAggregator.deploy(8, 4049610000); // 40.4961
await mockAggregator.deployed();
console.log(`Mock aggregator deployed at ${mockAggregator.address}`);
return mockAggregator.address;
}

module.exports = async function getPoRAddress(network) {
switch (network) {
case 'mainnet':
return '';
case 'sepolia':
return '';
case 'arbitrum':
return '0x47A2fBEb46553F01E7133686Fb1b5349d4823a0C';
case 'arbsepolia':
return '';
case 'base':
return '';
case 'basesepolia':
return '';
case 'localhost':
return await deployAndGetMockAggregatorAddress();
default:
throw new Error('Invalid network for Chainlink PoR address');
}
};
25 changes: 0 additions & 25 deletions scripts/helpers/chainlink-pricefeed-addresses.js

This file was deleted.

Loading

0 comments on commit 83af649

Please sign in to comment.