Skip to content
Merged
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
28 changes: 6 additions & 22 deletions contracts/protocol/bases/mixins/FundsManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import { FermionStorage } from "../../libs/Storage.sol";
import { ContextLib } from "../../libs/ContextLib.sol";
import { FermionFNFTLib } from "../../libs/FermionFNFTLib.sol";
import { IFermionFNFT } from "../../interfaces/IFermionFNFT.sol";

Check warning on line 13 in contracts/protocol/bases/mixins/FundsManager.sol

View workflow job for this annotation

GitHub Actions / setup

imported name IFermionFNFT is not used
import { IFundsEvents } from "../../interfaces/events/IFundsEvents.sol";

/**
Expand Down Expand Up @@ -74,23 +74,12 @@
* @param _amount - amount to be transferred
*/
function transferERC20ToProtocol(address _tokenAddress, address _from, uint256 _amount) internal {
// protocol balance before the transfer
uint256 protocolTokenBalanceBefore;
uint256 protocolTokenBalanceAfter;
// prevent ERC721 deposits
isERC721Contract(_tokenAddress, false);

// transfer ERC20 tokens from the caller
if (checkFNFTContract(_tokenAddress)) {
protocolTokenBalanceBefore = IFermionFNFT(_tokenAddress).balanceOfERC20(address(this));
_tokenAddress.transferFrom(_from, address(this), _amount);
protocolTokenBalanceAfter = IFermionFNFT(_tokenAddress).balanceOfERC20(address(this));
} else {
// prevent ERC721 deposits
isERC721Contract(_tokenAddress, false);

protocolTokenBalanceBefore = IERC20(_tokenAddress).balanceOf(address(this));
IERC20(_tokenAddress).safeTransferFrom(_from, address(this), _amount);
protocolTokenBalanceAfter = IERC20(_tokenAddress).balanceOf(address(this));
}
uint256 protocolTokenBalanceBefore = IERC20(_tokenAddress).balanceOf(address(this));
IERC20(_tokenAddress).safeTransferFrom(_from, address(this), _amount);
uint256 protocolTokenBalanceAfter = IERC20(_tokenAddress).balanceOf(address(this));

// make sure that expected amount of tokens was transferred
uint256 receivedAmount = protocolTokenBalanceAfter - protocolTokenBalanceBefore;
Expand Down Expand Up @@ -175,12 +164,7 @@
(bool success, bytes memory errorMessage) = _to.call{ value: _amount }("");
if (!success) revert FundsErrors.TokenTransferFailed(_to, _amount, errorMessage);
} else {
// transfer ERC20 tokens
if (checkFNFTContract(_tokenAddress)) {
_tokenAddress.transfer(_to, _amount);
} else {
IERC20(_tokenAddress).safeTransfer(_to, _amount);
}
IERC20(_tokenAddress).safeTransfer(_to, _amount);
}
}

Expand Down
83 changes: 80 additions & 3 deletions contracts/protocol/clients/Common.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import { ERC721Upgradeable as ERC721 } from "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol";
import { FermionTypes } from "../domain/Types.sol";
import { FractionalisationErrors } from "../domain/Errors.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { FermionFractionsERC20 } from "./FermionFractionsERC20.sol";

error InvalidStateOrCaller(uint256 tokenId, address sender, FermionTypes.TokenState state);
event TokenStateChange(uint256 indexed tokenId, FermionTypes.TokenState state);
Expand All @@ -20,7 +22,7 @@
}

// keccak256(abi.encode(uint256(keccak256("fermion.common.storage")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant CommonStorageLocation = 0x7d46dfbe85229102c9de7236c77f143aeebfb8807e422547099ad6d89710cd00;

Check warning on line 25 in contracts/protocol/clients/Common.sol

View workflow job for this annotation

GitHub Actions / setup

Constant name must be in capitalized SNAKE_CASE

function _getFermionCommonStorage() internal pure returns (CommonStorage storage $) {
assembly {
Expand All @@ -28,8 +30,18 @@
}
}

// keccak256(abi.encode(uint256(keccak256("fermion.fractions.storage")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant FermionFractionsStorageLocation =

Check warning on line 34 in contracts/protocol/clients/Common.sol

View workflow job for this annotation

GitHub Actions / setup

Constant name must be in capitalized SNAKE_CASE
0x4a7c305e00776741ac7013c3447ca536097b753ba0aa5e566dd79e90f6126200;

function _getFermionFractionsStorage() internal pure returns (FermionTypes.FermionFractionsStorage storage $) {
assembly {
$.slot := FermionFractionsStorageLocation
}
}

// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ERC721")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant ERC721StorageLocation = 0x80bb2b638cc20bc4d0a60d66940f3ab4a00c1d7b313497ca82fb0b4ab0079300;

Check warning on line 44 in contracts/protocol/clients/Common.sol

View workflow job for this annotation

GitHub Actions / setup

Constant name must be in capitalized SNAKE_CASE

function _getERC721Storage() internal pure returns (ERC721.ERC721Storage storage $) {
assembly {
Expand All @@ -37,13 +49,40 @@
}
}

// NOTE: storing the initial buyout auction here when epoch = 0
// keccak256(abi.encode(uint256(keccak256("fermion.buyout.auction.storage")) - 1)) & ~bytes32(uint256(0xff));
bytes32 private constant BuyoutAuctionStorageLocation =

Check warning on line 54 in contracts/protocol/clients/Common.sol

View workflow job for this annotation

GitHub Actions / setup

Constant name must be in capitalized SNAKE_CASE
0x224d6815573209d133aab26f2f52964556d2c06abbb82d0961460cd2e673cd00;

function _getBuyoutAuctionStorage() internal pure returns (FermionTypes.BuyoutAuctionStorage storage $) {
assembly {
$.slot := BuyoutAuctionStorageLocation
// NOTE: pointer to mapping(uint256 epoch => BuyoutAuctionStorageLocation)
// keccak256(abi.encode(uint256(keccak256("fermion.buyout.auction.storage.epochs")) - 1)) & ~bytes32(uint256(0xff));
bytes32 private constant BuyoutAuctionStorageEpochsLocation =

Check warning on line 59 in contracts/protocol/clients/Common.sol

View workflow job for this annotation

GitHub Actions / setup

Constant name must be in capitalized SNAKE_CASE
0x7ea90b6250b50fe4d5b733a96ceefb11247134d8b2fa5878319c586d1d546a00;

/**
* @notice Get the buyout auction storage for a specific epoch
* @dev epoch = 0 is special case and stored directly in the storage location.
* epoch != 0 is stored in the mapping(uint256 epoch => BuyoutAuctionStorageLocation)
* where BuyoutAuctionStorageEpochsLocation is the mapping pointer location
*
* @param _epoch The epoch
* @return $ The storage
*/
function _getBuyoutAuctionStorage(
uint256 _epoch
) internal pure returns (FermionTypes.BuyoutAuctionStorage storage $) {
if (_epoch == 0) {
assembly {
$.slot := BuyoutAuctionStorageLocation
}
} else {
// Calculate the storage slot directly in assembly instead of reading from storage.
// This is more efficient and avoids the need for external storage reads.
assembly {
mstore(0x00, _epoch)
mstore(0x20, BuyoutAuctionStorageEpochsLocation)
$.slot := keccak256(0x00, 0x40)
}
}
}

Expand Down Expand Up @@ -101,4 +140,42 @@
return auctions[auctions.length - 1];
}
}

/**
* @notice Returns the liquid number of fractions for current epoch. Represents fractions of F-NFTs that are fractionalised
* @dev This function is used in multiple contracts to calculate the available supply
* @param _epoch The epoch to check
* @return The liquid supply of fractions
*/
function liquidSupply(uint256 _epoch) internal view returns (uint256) {
FermionTypes.FermionFractionsStorage storage fractionStorage = _getFermionFractionsStorage();
if (_epoch >= fractionStorage.epochToClone.length) return 0;
address erc20Clone = fractionStorage.epochToClone[_epoch];

FermionTypes.BuyoutAuctionStorage storage $ = _getBuyoutAuctionStorage(_epoch);

return
IERC20(erc20Clone).totalSupply() -
$.unrestricedRedeemableSupply -
$.lockedRedeemableSupply -
$.pendingRedeemableSupply;
}

/**
* @notice Helper function to transfer fractions between addresses
* @param _from The address to transfer from
* @param _to The address to transfer to
* @param _amount The amount of fractions to transfer
* @param _epoch The epoch of the fractions
*/
function _transferFractions(address _from, address _to, uint256 _amount, uint256 _epoch) internal {
FermionTypes.FermionFractionsStorage storage fractionStorage = _getFermionFractionsStorage();
address erc20Clone = fractionStorage.epochToClone[_epoch];

if (_from == address(this)) {
FermionFractionsERC20(erc20Clone).transfer(_to, _amount);
} else {
FermionFractionsERC20(erc20Clone).transferFractionsFrom(_from, _to, _amount);
}
}
}
Loading
Loading