-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[WIP] New V2 comptrollers for dHEDGE deprecated vault buyback support
- Loading branch information
1 parent
8b1d697
commit c4ce19e
Showing
10 changed files
with
700 additions
and
124 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
.vscode | ||
node_modules | ||
artifacts | ||
cache | ||
out | ||
*.md | ||
*.yml | ||
*.json |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
{ | ||
"plugins": ["prettier-plugin-solidity"], | ||
"printWidth": 120, | ||
"overrides": [ | ||
{ | ||
"files": "*.sol", | ||
"options": { | ||
"tabWidth": 4 | ||
} | ||
} | ||
] | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
{ | ||
"extends": "solhint:recommended", | ||
"rules": { | ||
"compiler-version": ["error", "0.8.18"], | ||
"func-visibility": ["warn", { "ignoreConstructors": true }], | ||
"private-vars-leading-underscore": ["warn", { "strict": false }], | ||
"func-param-name-mixedcase": ["warn"], | ||
"modifier-name-mixedcase": ["warn"], | ||
"named-parameters-mapping": ["warn"], | ||
"gas-named-return-values": ["warn"], | ||
"max-states-count": "off", | ||
"interface-starts-with-i": "warn", | ||
"gas-calldata-parameters": "warn", | ||
"gas-struct-packing": "warn" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
lib/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,6 +3,7 @@ src = 'src' | |
out = 'out' | ||
libs = ['lib'] | ||
verbosity = 2 | ||
auto_detect_solc = true | ||
|
||
[fuzz] | ||
runs = 1_000 | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
{ | ||
"remappings": [ | ||
"openzeppelin-contracts-upgradeable/contracts/access/OwnableUpgradeable.sol=/share/lib/openzeppelin-contracts-upgradeable/contracts/access/OwnableUpgradeable.sol", | ||
"openzeppelin-contracts-upgradeable/contracts/security/PausableUpgradeable.sol=/share/lib/openzeppelin-contracts-upgradeable/contracts/security/PausableUpgradeable.sol", | ||
"openzeppelin-contracts-upgradeable/contracts/token/ERC20/utils/SafeERC20Upgradeable.sol=/share/lib/openzeppelin-contracts-upgradeable/contracts/token/ERC20/utils/SafeERC20Upgradeable.sol", | ||
"openzeppelin-contracts-upgradeable/contracts/interfaces/IERC20Upgradeable.sol=/share/lib/openzeppelin-contracts-upgradeable/contracts/interfaces/IERC20Upgradeable.sol", | ||
"./interfaces/IERC20Burnable.sol=/share/src/interfaces/IERC20Burnable.sol", | ||
"./interfaces/ICrossDomainMessenger.sol=/share/src/interfaces/ICrossDomainMessenger.sol", | ||
"./interfaces/IPoolLogic.sol=/share/src/interfaces/IPoolLogic.sol" | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
{ | ||
"filter_paths": "lib", | ||
"solc_remaps": [ | ||
"openzeppelin-contracts-upgradeable/contracts/access/OwnableUpgradeable.sol=/share/lib/openzeppelin-contracts-upgradeable/contracts/access/OwnableUpgradeable.sol", | ||
"openzeppelin-contracts-upgradeable/contracts/security/PausableUpgradeable.sol=/share/lib/openzeppelin-contracts-upgradeable/contracts/security/PausableUpgradeable.sol", | ||
"openzeppelin-contracts-upgradeable/contracts/token/ERC20/utils/SafeERC20Upgradeable.sol=/share/lib/openzeppelin-contracts-upgradeable/contracts/token/ERC20/utils/SafeERC20Upgradeable.sol", | ||
"openzeppelin-contracts-upgradeable/contracts/interfaces/IERC20Upgradeable.sol=/share/lib/openzeppelin-contracts-upgradeable/contracts/interfaces/IERC20Upgradeable.sol", | ||
"./interfaces/IERC20Burnable.sol=/share/src/interfaces/IERC20Burnable.sol", | ||
"./interfaces/ICrossDomainMessenger.sol=/share/src/interfaces/ICrossDomainMessenger.sol", | ||
"./interfaces/IPoolLogic.sol=/share/src/interfaces/IPoolLogic.sol" | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,236 @@ | ||
// SPDX-License-Identifier: SEE LICENSE IN LICENSE | ||
pragma solidity 0.8.18; | ||
|
||
import {OwnableUpgradeable} from "openzeppelin-contracts-upgradeable/contracts/access/OwnableUpgradeable.sol"; | ||
import {PausableUpgradeable} from "openzeppelin-contracts-upgradeable/contracts/security/PausableUpgradeable.sol"; | ||
import {SafeERC20Upgradeable} from "openzeppelin-contracts-upgradeable/contracts/token/ERC20/utils/SafeERC20Upgradeable.sol"; | ||
import {IERC20Upgradeable} from "openzeppelin-contracts-upgradeable/contracts/interfaces/IERC20Upgradeable.sol"; | ||
import {IERC20Burnable} from "./interfaces/IERC20Burnable.sol"; | ||
import {ICrossDomainMessenger} from "./interfaces/ICrossDomainMessenger.sol"; | ||
|
||
/// @title L1 comptroller contract for token buy backs. | ||
/// @notice Contract to burn a token and claim another one on L2. | ||
/// @author dHEDGE | ||
/// @dev This contract is only useful if paired with the L2 comptroller. | ||
contract L1Comptroller is OwnableUpgradeable, PausableUpgradeable { | ||
using SafeERC20Upgradeable for IERC20Upgradeable; | ||
|
||
|
||
///////////////////////////////////////////// | ||
// Structs // | ||
///////////////////////////////////////////// | ||
|
||
struct BurnTokenDetails { | ||
bool isTokenToBurn; | ||
bool isIERC20Burnable; | ||
uint256 exchangeRate; | ||
} | ||
|
||
///////////////////////////////////////////// | ||
// Events // | ||
///////////////////////////////////////////// | ||
|
||
event L2ComptrollerSet(address newL2Comptroller); | ||
event CrossChainGasLimitModified(uint256 newCrossChainGasLimit); | ||
event EmergencyWithdrawal(address indexed token, uint256 amount); | ||
event BuyBackFromL1Initiated( | ||
address indexed depositor, | ||
address indexed tokenToBurn, | ||
address indexed receiver, | ||
uint256 burnTokenAmount, | ||
uint256 totalAmountBurnt | ||
); | ||
|
||
|
||
///////////////////////////////////////////// | ||
// Errors // | ||
///////////////////////////////////////////// | ||
|
||
error ZeroAddress(); | ||
error ZeroValue(); | ||
error L2ComptrollerNotSet(); | ||
error NotBuybackToken(address token); | ||
error NotEligibleBuybackPair(address tokenToBuy, address tokenToBurn); | ||
|
||
|
||
///////////////////////////////////////////// | ||
// Variables // | ||
///////////////////////////////////////////// | ||
|
||
address public constant BURN_ADDRESS = 0x000000000000000000000000000000000000dEaD; | ||
|
||
/// @notice The Optimism contract to interact with on L1 Ethereum for sending data using smart contracts. | ||
ICrossDomainMessenger public crossDomainMessenger; | ||
|
||
/// @notice Address of the L2 comptroller to be called to initiate a buyback claim. | ||
/// @dev Has to be set after deployment of both the contracts. | ||
address public l2Comptroller; | ||
|
||
/// @dev The gas limit to be used to call the Optimism Cross Domain Messenger contract. | ||
uint32 public crossChainCallGasLimit; | ||
|
||
/// @notice Mapping of token to be burnt and its details. | ||
/// @dev Note that we are not enforcing that the `token` be a burnable token. | ||
/// We can write different functions depending on the token type. | ||
mapping(address token => BurnTokenDetails burnTokenDetails) public tokensToBurn; | ||
|
||
/// @notice Mapping of token to buy and token to burn. | ||
/// @dev This mapping is used to check if a pair is valid for buyback. | ||
mapping(address tokenToBuy => mapping(address tokenToBurn => bool isAPair)) public tokenPairs; | ||
|
||
/// @notice Stores cumulative amount of tokens burnt by an address. | ||
/// @dev We don't need to use order IDs as the difference of `totalAmount` (burnt) on L1 | ||
/// and `totalAmount` (claimed) on L2 gives us the amount of buy tokens tokens yet to be claimed. | ||
/// @dev The `totalAmount` for an address would/should NEVER decrease. | ||
mapping(address depositor => mapping(address tokenToBurn => uint256 totalAmount)) public burntAmountOf; | ||
|
||
|
||
///////////////////////////////////////////// | ||
// Modifiers // | ||
///////////////////////////////////////////// | ||
|
||
/// @dev Modifier to check that l2Comptroller address has been set or not. | ||
modifier whenL2ComptrollerSet() { | ||
if (l2Comptroller == address(0)) revert L2ComptrollerNotSet(); | ||
_; | ||
} | ||
|
||
|
||
///////////////////////////////////////////// | ||
// Functions // | ||
///////////////////////////////////////////// | ||
|
||
/// @dev To prevent the implementation contract from being used, we invoke the _disableInitializers | ||
/// function in the constructor to automatically lock it when it is deployed. | ||
/// @custom:oz-upgrades-unsafe-allow constructor | ||
constructor() { | ||
_disableInitializers(); | ||
} | ||
|
||
/// @notice Function to initialize this contract. | ||
/// @param _crossDomainMessenger The cross domain messenger contract on L1. | ||
/// @param _crossChainCallGasLimit The gas limit to be passed for a cross chain call | ||
/// to the L2Comptroller contract. | ||
function initialize( | ||
ICrossDomainMessenger _crossDomainMessenger, | ||
uint32 _crossChainCallGasLimit | ||
) external initializer { | ||
if ( | ||
address(_crossDomainMessenger) == address(0) | ||
) revert ZeroAddress(); | ||
|
||
__Ownable_init(); | ||
__Pausable_init(); | ||
|
||
crossDomainMessenger = _crossDomainMessenger; | ||
crossChainCallGasLimit = _crossChainCallGasLimit; | ||
} | ||
|
||
/// @notice Function to burn `amount` of a `token` and claim against it on L2. | ||
/// @dev If a transaction passes on L1 but fails on L2 then the user must claim their share on L2 directly. | ||
/// @param tokenToBuy Address of the token to be claimed. | ||
/// @param tokenToBurn Address of the token to be burnt. | ||
/// @param receiver Address of the account which will receive the claim. | ||
/// @param burnTokenAmount Amount of `tokenToBurn` to be burnt. | ||
function buyBack( | ||
address tokenToBuy, | ||
address tokenToBurn, | ||
address receiver, | ||
uint256 burnTokenAmount | ||
) external whenNotPaused whenL2ComptrollerSet { | ||
if(tokenPairs[tokenToBuy][tokenToBurn] == false) revert NotEligibleBuybackPair(tokenToBuy, tokenToBurn); | ||
|
||
_burnToken(tokenToBurn, burnTokenAmount); | ||
|
||
uint256 totalBurntAmount = burntAmountOf[msg.sender][tokenToBurn] += burnTokenAmount; | ||
|
||
// Send a cross chain message to `l2Comptroller` for releasing the buy tokens. | ||
// TODO: Modify the following to call a different function in L2 comptroller. | ||
crossDomainMessenger.sendMessage( | ||
l2Comptroller, | ||
abi.encodeWithSignature( | ||
"buyBackFromL1(address,address,uint256)", | ||
msg.sender, | ||
receiver, | ||
totalBurntAmount | ||
), | ||
crossChainCallGasLimit | ||
); | ||
|
||
emit BuyBackFromL1Initiated(msg.sender, tokenToBurn, receiver, burnTokenAmount, totalBurntAmount); | ||
} | ||
|
||
function _burnToken(address token, uint256 amount) private { | ||
if(tokensToBurn[token].isTokenToBurn == false) revert NotBuybackToken(token); | ||
|
||
if(tokensToBurn[token].isIERC20Burnable) { | ||
IERC20Burnable(token).burnFrom(msg.sender, amount); | ||
} else { | ||
// If it's not a natively burnable token then transfer it to the burn address. | ||
IERC20Upgradeable(token).safeTransferFrom(msg.sender, BURN_ADDRESS, amount); | ||
} | ||
} | ||
|
||
///////////////////////////////////////////// | ||
// Owner Functions // | ||
///////////////////////////////////////////// | ||
|
||
/// @notice Function to set the L2 comptroller address deployed on Optimism. | ||
/// @dev This function needs to be called after deployment of both the contracts. | ||
/// @param newL2Comptroller Address of the newly deployed L2 comptroller. | ||
function setL2Comptroller(address newL2Comptroller) external onlyOwner { | ||
if (newL2Comptroller == address(0)) revert ZeroAddress(); | ||
|
||
l2Comptroller = newL2Comptroller; | ||
|
||
emit L2ComptrollerSet(newL2Comptroller); | ||
} | ||
|
||
/// @notice Function to withdraw tokens in an emergency situation. | ||
/// @param token Address of the token to be withdrawn. | ||
/// @param amount Amount of the `token` to be removed. | ||
function emergencyWithdraw( | ||
address token, | ||
uint256 amount | ||
) external onlyOwner { | ||
IERC20Upgradeable tokenToWithdraw = IERC20Upgradeable(token); | ||
|
||
// If the `amount` is max of uint256 then transfer all the available balance. | ||
if (amount == type(uint256).max) { | ||
amount = tokenToWithdraw.balanceOf(address(this)); | ||
} | ||
|
||
// NOTE: If the balanceOf(address(this)) < `amount` < type(uint256).max then | ||
// the transfer will revert. | ||
tokenToWithdraw.safeTransfer(owner(), amount); | ||
|
||
emit EmergencyWithdrawal(token, amount); | ||
} | ||
|
||
/// @notice Function to set the cross chain calls gas limit. | ||
/// @dev Optimism allows, upto a certain limit, free execution gas units on L2. | ||
/// This value is currently 1.92 million gas units. This might not be enough for us. | ||
/// Hence this function for modifying the gas limit. | ||
/// @param newCrossChainGasLimit The new gas amount to be sent to the l2Comptroller for cross chain calls. | ||
function setCrossChainGasLimit( | ||
uint32 newCrossChainGasLimit | ||
) external onlyOwner { | ||
if (newCrossChainGasLimit == 0) revert ZeroValue(); | ||
|
||
crossChainCallGasLimit = newCrossChainGasLimit; | ||
|
||
emit CrossChainGasLimitModified(newCrossChainGasLimit); | ||
} | ||
|
||
/// @notice Function to pause the critical functions in this contract. | ||
/// @dev This function won't make any state changes if already paused. | ||
function pause() external onlyOwner { | ||
_pause(); | ||
} | ||
|
||
/// @notice Function to unpause the critical functions in this contract. | ||
/// @dev This function won't make any state changes if already unpaused. | ||
function unpause() external onlyOwner { | ||
_unpause(); | ||
} | ||
} |
Oops, something went wrong.