Skip to content

Commit

Permalink
chore: cleanup and DLCManager update OZ libs
Browse files Browse the repository at this point in the history
  • Loading branch information
scolear committed Nov 20, 2024
1 parent f74faf3 commit 56f4372
Show file tree
Hide file tree
Showing 7 changed files with 184 additions and 54 deletions.
23 changes: 13 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
## Foundry
# dlc-symbiotic

**Foundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust.**
Repository for the dlc-link Symbiotic integration.

Foundry consists of:
## Usage

- **Forge**: Ethereum testing framework (like Truffle, Hardhat and DappTools).
- **Cast**: Swiss army knife for interacting with EVM smart contracts, sending transactions and getting chain data.
- **Anvil**: Local Ethereum node, akin to Ganache, Hardhat Network.
- **Chisel**: Fast, utilitarian, and verbose solidity REPL.
You can start a local Sepolia fork using:

## Documentation
```shell
anvil --fork-url https://eth-sepolia.g.alchemy.com/v2/your-api-key
```

https://book.getfoundry.sh/
And deploy the NetworkMiddleware contract using:

## Usage
```shell
forge script SetupNetworkMiddleware --rpc-url http://localhost:8545 --broadcast -vvvv --private-key 0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d
```

## Development

### Build

Expand Down
2 changes: 1 addition & 1 deletion src/SimpleMiddleware.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {ISlasher} from "@symbiotic/interfaces/slasher/ISlasher.sol";
import {IVetoSlasher} from "@symbiotic/interfaces/slasher/IVetoSlasher.sol";
import {Subnetwork} from "@symbiotic/contracts/libraries/Subnetwork.sol";

import {SimpleKeyRegistry32} from "./SimpleKeyRegistry32.sol";
import {SimpleKeyRegistry32} from "./libraries/SimpleKeyRegistry32.sol";
import {MapWithTimeData} from "./libraries/MapWithTimeData.sol";

contract SimpleMiddleware is SimpleKeyRegistry32, Ownable {
Expand Down
File renamed without changes.
150 changes: 117 additions & 33 deletions src/DLCManager.sol → src/dlc-core/DLCManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,21 @@

pragma solidity 0.8.25;

import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts-upgradeable/access/extensions/AccessControlDefaultAdminRulesUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol";
import "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol";
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "./DLCLinkLibrary.sol";
import "./DLCBTC.sol";
import "./IBTC.sol";

import "../libraries/AggregatorV3Interface.sol";

/**
* @author DLC.Link 2024
* @title DLCManager
* @dev This is the contract the Attestor Layer listens to.
* @dev This is the contract the Attestor Layer listens and writes to
* @dev It is upgradable through the OpenZeppelin proxy pattern
* @notice DLCManager is the main contract of the DLC.Link protocol.
* @custom:contact eng@dlc.link
Expand All @@ -28,7 +30,7 @@ import "./DLCBTC.sol";
contract DLCManager is Initializable, AccessControlDefaultAdminRulesUpgradeable, PausableUpgradeable {
using DLCLink for DLCLink.DLC;
using DLCLink for DLCLink.DLCStatus;
using SafeERC20 for DLCBTC;
using SafeERC20 for IBTC;

////////////////////////////////////////////////////////////////
// STATE VARIABLES //
Expand All @@ -48,7 +50,9 @@ contract DLCManager is Initializable, AccessControlDefaultAdminRulesUpgradeable,
bytes32 public tssCommitment;
string public attestorGroupPubKey;

DLCBTC public dlcBTC; // dlcBTC contract
// iBTC was historically called dlcBTC.
// Due the nature of upgradability, we have to keep the old name.
IBTC public dlcBTC; // iBTC contract.
string public btcFeeRecipient; // BTC address to send fees to
uint256 public minimumDeposit; // in sats
uint256 public maximumDeposit; // in sats
Expand All @@ -58,14 +62,19 @@ contract DLCManager is Initializable, AccessControlDefaultAdminRulesUpgradeable,

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

////////////////////////////////////////////////////////////////
// ERRORS //
////////////////////////////////////////////////////////////////

error NotDLCAdmin();
error IncompatibleRoles();
error NoSignerRenouncement();
error ContractNotWhitelisted();
error NotCreatorContract();
error DLCNotFound();
Expand All @@ -79,6 +88,7 @@ contract DLCManager is Initializable, AccessControlDefaultAdminRulesUpgradeable,
error NotEnoughSignatures();
error InvalidSigner();
error DuplicateSignature();
error DuplicateSigner(address signer);
error SignerNotApproved(address signer);
error ClosingFundedVault();

Expand All @@ -91,6 +101,7 @@ contract DLCManager is Initializable, AccessControlDefaultAdminRulesUpgradeable,
error InsufficientMintedBalance(uint256 minted, uint256 amount);
error FeeRateOutOfBounds(uint256 feeRate);
error UnderCollateralized(uint256 newValueLocked, uint256 valueMinted);
error NotEnoughReserves(uint256 reserves, uint256 amount);

////////////////////////////////////////////////////////////////
// MODIFIERS //
Expand All @@ -116,15 +127,15 @@ contract DLCManager is Initializable, AccessControlDefaultAdminRulesUpgradeable,
modifier onlyVaultCreator(
bytes32 _uuid
) {
if (dlcs[dlcIDsByUUID[_uuid]].creator != tx.origin) revert NotOwner();
if (dlcs[dlcIDsByUUID[_uuid]].creator != msg.sender) revert NotOwner();
_;
}

function initialize(
address defaultAdmin,
address dlcAdminRole,
uint16 threshold,
DLCBTC tokenContract,
IBTC tokenContract,
string memory btcFeeRecipientToSet
) public initializer {
__AccessControlDefaultAdminRules_init(2 days, defaultAdmin);
Expand All @@ -143,6 +154,22 @@ contract DLCManager is Initializable, AccessControlDefaultAdminRulesUpgradeable,
btcMintFeeRate = 12; // 0.12% BTC fee for now
btcRedeemFeeRate = 15; // 0.15% BTC fee for now
btcFeeRecipient = btcFeeRecipientToSet;
porEnabled = false;
totalValueMinted = 0;
}

/**
* @notice Initialize total minted value tracking
* @dev This function is called once after the contract is upgraded with totalValueMinted tracking
*/
function initializeV2() public reinitializer(2) {
// Calculate initial total by iterating through existing vaults
uint256 total = 0;
for (uint256 i = 0; i < _index; i++) {
total += dlcs[i].valueMinted;
}

totalValueMinted = total;
}

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

////////////////////////////////////////////////////////////////
// INTERNAL FUNCTIONS //
Expand All @@ -188,42 +217,72 @@ contract DLCManager is Initializable, AccessControlDefaultAdminRulesUpgradeable,
* @notice Checks the 'signatures' of Attestors for a given 'message'.
* @dev Recalculates the hash to make sure the signatures are for the same message.
* @dev Uses OpenZeppelin's ECDSA library to recover the public keys from the signatures.
* @dev Signatures must be unique.
* @dev Signatures must be unique, from unique signers.
* @param message Original message that was signed.
* @param signatures Byte array of at least 'threshold' number of signatures.
*/
function _attestorMultisigIsValid(bytes memory message, bytes[] memory signatures) internal view {
function _attestorMultisigIsValid(bytes memory message, bytes[] memory signatures) internal {
if (signatures.length < _threshold) revert NotEnoughSignatures();

bytes32 prefixedMessageHash = MessageHashUtils.toEthSignedMessageHash(keccak256(message));

if (_hasDuplicates(signatures)) revert DuplicateSignature();

for (uint256 i = 0; i < signatures.length; i++) {
address attestorPubKey = ECDSA.recover(prefixedMessageHash, signatures[i]);
if (!hasRole(APPROVED_SIGNER, attestorPubKey)) {
revert InvalidSigner();
}
_checkSignerUnique(attestorPubKey, prefixedMessageHash);
}
}

function _checkSignerUnique(address attestorPubKey, bytes32 messageHash) internal {
if (_seenSigners[attestorPubKey][messageHash]) {
revert DuplicateSigner(attestorPubKey);
}
_seenSigners[attestorPubKey][messageHash] = true;
}

/**
* @notice Checks for duplicate values in an array.
* @dev Used to check for duplicate signatures.
* @param signatures Array of signatures.
* @return bool True if there are duplicates, false otherwise.
* @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 _hasDuplicates(
bytes[] memory signatures
) internal pure returns (bool) {
for (uint256 i = 0; i < signatures.length - 1; i++) {
for (uint256 j = i + 1; j < signatures.length; j++) {
if (keccak256(signatures[i]) == keccak256(signatures[j])) {
return true;
}
}
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;
}
return false;

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

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

function _mintTokens(address to, uint256 amount) internal {
Expand All @@ -245,15 +304,15 @@ contract DLCManager is Initializable, AccessControlDefaultAdminRulesUpgradeable,
* @return bytes32 uuid of the new vault/DLC
*/
function setupVault() external whenNotPaused onlyWhitelisted returns (bytes32) {
bytes32 _uuid = _generateUUID(tx.origin, _index);
bytes32 _uuid = _generateUUID(msg.sender, _index);

dlcs[_index] = DLCLink.DLC({
uuid: _uuid,
protocolContract: msg.sender, // deprecated
valueLocked: 0,
valueMinted: 0,
timestamp: block.timestamp,
creator: tx.origin,
creator: msg.sender,
status: DLCLink.DLCStatus.READY,
fundingTxId: "",
closingTxId: "",
Expand All @@ -264,10 +323,10 @@ contract DLCManager is Initializable, AccessControlDefaultAdminRulesUpgradeable,
taprootPubKey: ""
});

emit CreateDLC(_uuid, tx.origin, block.timestamp);
emit CreateDLC(_uuid, msg.sender, block.timestamp);

dlcIDsByUUID[_uuid] = _index;
userVaults[tx.origin].push(_uuid);
userVaults[msg.sender].push(_uuid);
_index++;

return _uuid;
Expand Down Expand Up @@ -319,7 +378,10 @@ contract DLCManager is Initializable, AccessControlDefaultAdminRulesUpgradeable,
dlc.valueLocked = newValueLocked;
dlc.valueMinted = newValueLocked;

_mintTokens(dlc.creator, amountToMint);
if (_checkMint(amountToMint, totalValueMinted)) {
totalValueMinted = totalValueMinted + amountToMint;
_mintTokens(dlc.creator, amountToMint);
}

emit SetStatusFunded(uuid, btcTxId, dlc.creator, newValueLocked, amountToMint);
}
Expand Down Expand Up @@ -375,6 +437,7 @@ contract DLCManager is Initializable, AccessControlDefaultAdminRulesUpgradeable,
}

dlc.valueMinted -= amount;
totalValueMinted -= amount;
_burnTokens(dlc.creator, amount);
emit Withdraw(uuid, amount, msg.sender);
}
Expand Down Expand Up @@ -488,6 +551,13 @@ contract DLCManager is Initializable, AccessControlDefaultAdminRulesUpgradeable,
}
}

function renounceRole(bytes32 role, address account) public override {
if (account == msg.sender && role == APPROVED_SIGNER) {
revert NoSignerRenouncement();
}
super.renounceRole(role, account);
}

function pauseContract() external onlyAdmin {
_pause();
}
Expand Down Expand Up @@ -600,4 +670,18 @@ contract DLCManager is Initializable, AccessControlDefaultAdminRulesUpgradeable,
) 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);
}
}
Loading

0 comments on commit 56f4372

Please sign in to comment.