-
Notifications
You must be signed in to change notification settings - Fork 7
feature: Frax USD Module Support #82
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
shinchann221
wants to merge
16
commits into
master
Choose a base branch
from
feature/frax-support
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 1 commit
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
dd74333
created frax module
shinchann221 d59050d
added validation for usdc withdrawal amount
shinchann221 01b7350
added test cases
shinchann221 1c4a7f4
removed test.txt
shinchann221 82a0085
added deploy script for dev
shinchann221 0e4a986
added usdt price oracle for frax
shinchann221 ba64f82
frax module deployed
shivam-ef 477dad6
fix: test for frax
shivam-ef 61f7010
updated fraxmodule for async withdrawal
shinchann221 e88332c
updated async withdrawals
shinchann221 aa74276
resolved decimal
shinchann221 bfe994c
Merge branch 'master' into feature/frax-support
shinchann221 e0a75b4
resolved review comments
shinchann221 e5afcf7
fix: bytes32 coversion
shinchann221 84b33d3
fix: audit issues & updated test cases
shinchann221 da81d2c
feat: added gnosis scripts
shinchann221 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or 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,6 @@ | ||
| @openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/ | ||
| @openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/ | ||
| forge-std/=lib/forge-std/src/ | ||
| openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/ | ||
| openzeppelin-contracts/=lib/openzeppelin-contracts/ | ||
| solady/=lib/solady/src/ |
This file contains hidden or 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,9 @@ | ||
| // SPDX-License-Identifier: MIT | ||
| pragma solidity ^0.8.28; | ||
|
|
||
| import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; | ||
|
|
||
| interface IFraxCustodian { | ||
| function deposit(uint256 amountIn, address reciever) external payable returns (uint256 shares); | ||
| function redeem(uint256 sharesIn, address reciever, address owner) external returns (uint256 amountOut); | ||
| } |
This file contains hidden or 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,183 @@ | ||
| // SPDX-License-Identifier: MIT | ||
| pragma solidity ^0.8.28; | ||
|
|
||
| import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; | ||
| import { MessageHashUtils } from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol"; | ||
| import { SafeCast } from "@openzeppelin/contracts/utils/math/SafeCast.sol"; | ||
| import { ReentrancyGuardTransient } from "@openzeppelin/contracts/utils/ReentrancyGuardTransient.sol"; | ||
|
|
||
| import { IEtherFiSafe } from "../../interfaces/IEtherFiSafe.sol"; | ||
| import { IFraxCustodian } from "../../interfaces/IFraxCustodian.sol"; | ||
| import { IRoleRegistry } from "../../interfaces/IRoleRegistry.sol"; | ||
| import { ModuleBase } from "../ModuleBase.sol"; | ||
| import { ModuleCheckBalance } from "../ModuleCheckBalance.sol"; | ||
|
|
||
|
|
||
| /** | ||
| * @title FraxModule | ||
| * @author ether.fi | ||
| * @notice Module for interacting with FraxUSD | ||
| * @dev Extends ModuleBase to provide FraxUSD integration for Safes | ||
| */ | ||
| contract FraxModule is ModuleBase, ModuleCheckBalance, ReentrancyGuardTransient { | ||
| using MessageHashUtils for bytes32; | ||
| using SafeCast for uint256; | ||
|
|
||
| address public immutable fraxusd; | ||
|
|
||
| address public immutable usdc; | ||
|
|
||
| address public immutable custodian; | ||
|
|
||
| /// @notice TypeHash for deposit function signature | ||
| bytes32 public constant DEPOSIT_SIG = keccak256("deposit"); | ||
|
|
||
| /// @notice TypeHash for withdraw function signature | ||
| bytes32 public constant WITHDRAW_SIG = keccak256("withdraw"); | ||
|
|
||
| //Todo: check which role will be the admin for Frax Module | ||
| /// @notice Role identifier for admins of the Liquid Module | ||
| bytes32 public constant ETHERFI_LIQUID_MODULE_ADMIN = keccak256("ETHERFI_LIQUID_MODULE_ADMIN"); | ||
|
|
||
| /// @notice Emitted when safe deposits into Liquid | ||
| event LiquidDeposit(address indexed safe, address indexed inputToken, address indexed outputToken, uint256 inputAmount, uint256 outputAmount); | ||
|
|
||
| /// @notice Emitted when safe withdraws from Liquid | ||
| event LiquidWithdrawal(address indexed safe, address indexed liquidAsset, uint256 amountToWithdraw, uint256 amountOut); | ||
|
|
||
| /// @notice Error when the return amount is less than min return | ||
| error InsufficientReturnAmount(); | ||
|
|
||
| /** | ||
| * @notice Contract constructor | ||
| * @param _fraxusd Address of the FRAXUSD token | ||
| * @param _usdc Addresses of the USDC token | ||
| * @param _etherFiDataProvider Address of the EtherFiDataProvider contract | ||
| * @param _custodian Address of the FraxUSD custodian | ||
| * @dev Initializes the contract with supported tokens | ||
| * @custom:throws InvalidInput If any provided address is zero | ||
| */ | ||
| constructor(address _fraxusd, address _usdc, address _etherFiDataProvider, address _custodian) ModuleBase(_etherFiDataProvider) ModuleCheckBalance(_etherFiDataProvider) { | ||
| if (_etherFiDataProvider == address(0) || _fraxusd == address(0) || _usdc == address(0)) revert InvalidInput(); | ||
|
|
||
| fraxusd = _fraxusd; | ||
| usdc = _usdc; | ||
| custodian = _custodian; | ||
| } | ||
|
|
||
| /** | ||
| * @notice Deposits USDC and mints FraxUSD using signature verification | ||
| * @param safe The Safe address which holds the USDC tokens | ||
| * @param amountToDeposit The amount of USDC tokens to deposit | ||
| * @param signer The address that signed the transaction | ||
| * @param signature The signature authorizing the transaction | ||
| * @dev Verifies signature then executes token approval and deposit through the Safe's module execution | ||
| * @custom:throws InvalidInput If amount is zero | ||
| * @custom:throws InvalidSignature If the signature is invalid | ||
| */ | ||
| function deposit(address safe, uint256 amountToDeposit, address signer, bytes calldata signature) external onlyEtherFiSafe(safe) onlySafeAdmin(safe, signer) { | ||
| bytes32 digestHash = _getDepositDigestHash(safe, amountToDeposit); | ||
| _verifyAdminSig(digestHash, signer, signature); | ||
| _deposit(safe, amountToDeposit); | ||
| } | ||
|
|
||
| /** | ||
| * @dev Creates a digest hash for the deposit operation | ||
| * @param safe The Safe address which holds the USDC tokens | ||
| * @param amountToDeposit The amount to deposit | ||
| * @return The digest hash for signature verification | ||
| */ | ||
| function _getDepositDigestHash(address safe, uint256 amountToDeposit) internal returns (bytes32) { | ||
| return keccak256(abi.encodePacked(DEPOSIT_SIG, block.chainid, address(this), _useNonce(safe), safe, abi.encode(amountToDeposit))).toEthSignedMessageHash(); | ||
| } | ||
|
|
||
| /** | ||
| * @dev Internal function to deposit USDC to FraxUSD custodian | ||
| * @param safe The Safe address which holds the USDC tokens | ||
| * @param amountToDeposit The amount of USDC tokens to deposit | ||
| * @custom:throws InvalidInput If amount or min return is zero | ||
| */ | ||
| function _deposit(address safe, uint256 amountToDeposit) internal { | ||
|
|
||
| if (amountToDeposit == 0) revert InvalidInput(); | ||
|
|
||
| _checkAmountAvailable(safe, usdc, amountToDeposit); | ||
|
|
||
| address[] memory to; | ||
| bytes[] memory data; | ||
| uint256[] memory values; | ||
|
|
||
| to = new address[](2); | ||
| data = new bytes[](2); | ||
| values = new uint256[](2); | ||
|
|
||
| to[0] = usdc; | ||
| data[0] = abi.encodeWithSelector(ERC20.approve.selector, address(custodian), amountToDeposit); | ||
|
|
||
| to[1] = address(custodian); | ||
| data[1] = abi.encodeWithSelector(IFraxCustodian.deposit.selector, amountToDeposit, safe); | ||
|
|
||
|
|
||
| uint256 liquidTokenBalBefore = ERC20(fraxusd).balanceOf(safe); | ||
|
|
||
| IEtherFiSafe(safe).execTransactionFromModule(to, values, data); | ||
|
|
||
| uint256 liquidTokenReceived = ERC20(fraxusd).balanceOf(safe) - liquidTokenBalBefore; | ||
| if (liquidTokenReceived < amountToDeposit) revert InsufficientReturnAmount(); | ||
|
|
||
| emit LiquidDeposit(safe, usdc, fraxusd, amountToDeposit, liquidTokenReceived); | ||
| } | ||
|
|
||
| /** | ||
| * @notice Withdraws from FraxUSD from the safe | ||
| * @param safe The Safe address which holds the FraxUSD tokens | ||
| * @param amountToWithdraw The amount of FraxUSD to withdraw | ||
| * @param signer The address that signed the transaction | ||
| * @param signature The signature authorizing the transaction | ||
| * @dev Verifies signature then executes token approval and deposit through the Safe's module execution | ||
| * @custom:throws InvalidInput If amount or min return is zero | ||
| * @custom:throws InvalidSignature If the signature is invalid | ||
| */ | ||
| function withdraw(address safe, uint128 amountToWithdraw, address signer, bytes calldata signature) external onlyEtherFiSafe(safe) onlySafeAdmin(safe, signer) { | ||
| bytes32 digestHash = _getWithdrawDigestHash(safe, amountToWithdraw); | ||
shinchann221 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| _verifyAdminSig(digestHash, signer, signature); | ||
| _withdraw(safe, amountToWithdraw); | ||
| } | ||
|
|
||
| /** | ||
| * @dev Creates a digest hash for the withdraw operation | ||
| * @param safe The Safe address which holds the FraxUSD tokens | ||
| * @param amountToWithdraw The amount to withdraw | ||
| * @return The digest hash for signature verification | ||
| */ | ||
| function _getWithdrawDigestHash(address safe, uint128 amountToWithdraw) internal returns (bytes32) { | ||
| return keccak256(abi.encodePacked(WITHDRAW_SIG, block.chainid, address(this), _useNonce(safe), safe, abi.encode(amountToWithdraw))).toEthSignedMessageHash(); | ||
| } | ||
|
|
||
| /** | ||
| * @notice Internal function which facilitates ithdrawals from the safe | ||
| * @param safe The Safe address which holds the FraxUSD tokens | ||
| * @param amountToWithdraw The amount of FraxuSD tokens to withdraw | ||
| * @custom:throws InvalidInput If the Safe doesn't have enough liquid asset balance | ||
| * @custom:throws InvalidSignature If the signature is invalid | ||
| */ | ||
| function _withdraw(address safe, uint128 amountToWithdraw) internal { | ||
| if (amountToWithdraw == 0) revert InvalidInput(); | ||
|
|
||
| _checkAmountAvailable(safe, fraxusd, amountToWithdraw); | ||
|
|
||
| address[] memory to = new address[](2); | ||
| bytes[] memory data = new bytes[](2); | ||
| uint256[] memory values = new uint256[](2); | ||
|
|
||
| to[0] = fraxusd; | ||
| data[0] = abi.encodeWithSelector(ERC20.approve.selector, custodian, amountToWithdraw); | ||
|
|
||
| to[1] = address(custodian); | ||
| data[1] = abi.encodeWithSelector(IFraxCustodian.redeem.selector, amountToWithdraw, safe, safe); | ||
|
|
||
| IEtherFiSafe(safe).execTransactionFromModule(to, values, data); | ||
|
|
||
| emit LiquidWithdrawal(safe, fraxusd, amountToWithdraw, amountToWithdraw); | ||
shinchann221 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } | ||
| } | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.