Description:
Multi-signature wallet contract requiring multiple confirmations for transaction execution.
Blockchain: Ethereum
Source Code: View Code On The Blockchain
Solidity Source Code:
{{
"language": "Solidity",
"sources": {
"src/deps/CustodianWithReceiver.sol": {
"content": "// SPDX-License-Identifier: MIT
// @version 0.2.8
pragma solidity >=0.8.0;
/**
* ====================================================================
* | ______ _______ |
* | / _____________ __ __ / ____(_____ ____ _____ ________ |
* | / /_ / ___/ __ `| |/_/ / /_ / / __ \/ __ `/ __ \/ ___/ _ \ |
* | / __/ / / / /_/ _> < / __/ / / / / / /_/ / / / / /__/ __/ |
* | /_/ /_/ \__,_/_/|_| /_/ /_/_/ /_/\__,_/_/ /_/\___/\___/ |
* | |
* ====================================================================
* =================== FrxUSDCustodianWithReceiver ====================
* ====================================================================
* FrxUSD Custodian contract for the Frax protocol to custody tokens at a 1-1 ratio
* Frax Finance: https://github.com/FraxFinance
*/
import { FrxUSDCustodian, IERC20 } from "src/deps/FrxUSDCustodian.sol";
contract FrxUSDCustodianWithReceiver is FrxUSDCustodian {
constructor(address _frxUSD, address _custodianTkn) FrxUSDCustodian(_frxUSD, _custodianTkn) {}
function onERC721Received(
address operator,
address from,
uint256 tokenId,
bytes calldata data
) external pure returns (bytes4) {
return FrxUSDCustodianWithReceiver.onERC721Received.selector;
}
/// @notice Function to send usdc in this contract to the Wisdom tree on `On Receipt` address
/// @param amountUsdc The amount of USDC to subscribe
/// @dev Assumes required USDC is already present in this contract
/// @dev Assumes WTGXX will be minted to the whitelisted address who sends usdc to the
/// `onReceiptPrimaryOrderAddress`
function shuffleToWtgxx(uint256 amountUsdc) external {
if (msg.sender != 0x4F95C5bA0C7c69FB2f9340E190cCeE890B3bd87c) revert OnlyUsdcCustodian();
IERC20 USDC = IERC20(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48);
address onReceiptPrimaryOrderAddress = 0x63a8bb98D70d0aC73460d22b6756528a087ecBf8;
USDC.transfer(onReceiptPrimaryOrderAddress, amountUsdc);
emit UsdcShuffledToWtgxxAsync(amountUsdc);
}
/// @notice Overriden function not to be used in child
function shuffleToRwa(
uint256,
uint256,
uint8
) public override {
revert NotImplemented();
}
/// @notice Overriden function not to be used in child
function shuffleToRwa(
uint256,
uint256
) external override {
revert NotImplemented();
}
/// @notice Event emitted when usdc is sucessfully used to subscribe to wtgxx
event UsdcShuffledToWtgxxAsync(uint256 amountUsdcSent);
/// @notice Error Emitted when the `shuffleToWtgxx` caller is not approved
error OnlyUsdcCustodian();
/// @notice Error Emitted when calling function not intended for this instance
error NotImplemented();
}
"
},
"src/deps/FrxUSDCustodian.sol": {
"content": "// SPDX-License-Identifier: MIT
// @version 0.2.8
pragma solidity >=0.8.0;
/**
* ====================================================================
* | ______ _______ |
* | / _____________ __ __ / ____(_____ ____ _____ ________ |
* | / /_ / ___/ __ `| |/_/ / /_ / / __ \/ __ `/ __ \/ ___/ _ \ |
* | / __/ / / / /_/ _> < / __/ / / / / / /_/ / / / / /__/ __/ |
* | /_/ /_/ \__,_/_/|_| /_/ /_/_/ /_/\__,_/_/ /_/\___/\___/ |
* | |
* ====================================================================
* ========================= FrxUSDCustodian ==========================
* ====================================================================
* FrxUSD Custodian contract for the Frax protocol to custody tokens at a 1-1 ratio
* Frax Finance: https://github.com/FraxFinance
*/
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import { Ownable2Step } from "@openzeppelin/contracts/access/Ownable2Step.sol";
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { Math } from "@openzeppelin/contracts/utils/math/Math.sol";
import { ReentrancyGuard } from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { IFrxUSD } from "src/interfaces/IFrxUSD.sol";
import { IRWAIssuer } from "src/interfaces/IRWAIssuer.sol";
import { IWtgxxCustodian } from "src/interfaces/IWtgxxCustodian.sol";
contract FrxUSDCustodian is Ownable2Step, ReentrancyGuard {
using Math for uint256;
// STATE VARIABLES
// ===================================================
/// @notice frxUSD token = share
IFrxUSD public immutable frxUSD;
/// @notice Custodian token = asset
IERC20 public immutable custodianTkn;
/// @notice Decimals for the frxUSD
uint8 public immutable frxUSDDecimals;
/// @notice Decimals for the custodian token
uint8 public immutable custodianTknDecimals;
/// @notice If the contract was initialized
bool public wasInitialized;
/// @notice Fee for minting. 18 decimals
uint256 public mintFee;
/// @notice Fee for redeeming. 18 decimals
uint256 public redeemFee;
/// @notice Mint cap for frxUSD minting
uint256 public mintCap;
/// @notice frxUSD minted accounting
uint256 public frxUSDMinted;
/// @notice Mapping to indicated which bots are allowed to shuffle assets
mapping(address => bool) public isApprovedOperator;
/// @notice The minimum amount of `custodianTkn` that must remain in the contract on `shuffleToRwa`
uint256 public minAfterShuffle;
// CONSTRUCTOR & INITIALIZER
// ===================================================
/// @notice Contract constructor
constructor(address _frxUSD, address _custodianTkn) Ownable(msg.sender) {
// Set the contract as initialized
wasInitialized = true;
// Set token addresses
frxUSD = IFrxUSD(_frxUSD);
custodianTkn = IERC20(_custodianTkn);
// Set decimals
frxUSDDecimals = frxUSD.decimals();
custodianTknDecimals = ERC20(_custodianTkn).decimals();
}
/**
* @notice Initialize contract
* @param _owner The owner of this contract
* @param _mintCap The mint cap for frxUSD minting
* @param _mintFee The mint fee
* @param _redeemFee The redeem fee
*/
function initialize(address _owner, uint256 _mintCap, uint256 _mintFee, uint256 _redeemFee) public {
// Make sure the contract wasn't already initialized
if (wasInitialized) revert InitializeFailed();
if (_owner == address(0)) revert OwnerCannotInitToZeroAddress();
// Set owner for Ownable
_transferOwnership(_owner);
// Set the mint cap
mintCap = _mintCap;
// Set the mint/redeem fee
mintFee = _mintFee;
redeemFee = _redeemFee;
// Set the contract as initialized
wasInitialized = true;
}
// ERC4626 PUBLIC/EXTERNAL VIEWS
// ===================================================
/// @notice Return the underlying asset
/// @return _custodianTkn The custodianTkn asset
function asset() public view returns (address _custodianTkn) {
_custodianTkn = address(custodianTkn);
}
/// @notice Share balance of the supplied address
/// @param _addr The address to test
/// @return _balance Total amount of shares
function balanceOf(address _addr) public view returns (uint256 _balance) {
return frxUSD.balanceOf(_addr);
}
/// @notice Total amount of underlying asset available
/// @param _assets Amount of underlying tokens
/// @dev See {IERC4626-totalAssets}
function totalAssets() public view returns (uint256 _assets) {
return custodianTkn.balanceOf(address(this));
}
/// @notice Total amount of shares
/// @return _supply Total amount of shares
function totalSupply() public view returns (uint256 _supply) {
return frxUSD.totalSupply();
}
/// @notice Returns the amount of shares that the contract would exchange for the amount of assets provided
/// @param _assets Amount of underlying tokens
/// @return _shares Amount of shares that the underlying _assets represents
/// @dev See {IERC4626-convertToShares}
function convertToShares(uint256 _assets) public view returns (uint256 _shares) {
_shares = _convertToShares(_assets, Math.Rounding.Floor);
}
/// @notice Returns the amount of assets that the contract would exchange for the amount of shares provided
/// @param _shares Amount of shares
/// @return _assets Amount of underlying asset that _shares represents
/// @dev See {IERC4626-convertToAssets}
function convertToAssets(uint256 _shares) public view returns (uint256 _assets) {
_assets = _convertToAssets(_shares, Math.Rounding.Floor);
}
/// @notice Returns the maximum amount of the underlying asset that can be deposited into the contract for the receiver, through a deposit call. Includes fee.
/// @param _addr The address to test
/// @return _maxAssetsIn The max amount that can be deposited
/**
* @dev See {IERC4626-maxDeposit}
* Contract frxUSD -> custodianTkn needed
*/
function maxDeposit(address _addr) public view returns (uint256 _maxAssetsIn) {
// See how much custodianTkn you would need to exchange for 100% of the frxUSD available under the cap
if (frxUSDMinted >= mintCap) _maxAssetsIn = 0;
else _maxAssetsIn = previewMint(mintCap - frxUSDMinted);
}
/// @notice Returns the maximum amount of shares that can be minted for the receiver, through a mint call. Includes fee.
/// @param _addr The address to test
/// @return _maxSharesOut The max amount that can be minted
/**
* @dev See {IERC4626-maxMint}
* Contract frxUSD balance
*/
function maxMint(address _addr) public view returns (uint256 _maxSharesOut) {
// See how much frxUSD is actually available in the contract
if (frxUSDMinted >= mintCap) _maxSharesOut = 0;
else _maxSharesOut = mintCap - frxUSDMinted;
}
/// @notice Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the contract, through a withdraw call. Includes fee.
/// @param _owner The address to check
/// @return _maxAssetsOut The maximum amount of underlying asset that can be withdrawn
/**
* @dev See {IERC4626-maxWithdraw}
* Lesser of
* a) User frxUSD -> custodianTkn amount
* b) Contract custodianTkn balance
*/
function maxWithdraw(address _owner) public view returns (uint256 _maxAssetsOut) {
// See how much custodianTkn the user could possibly withdraw with 100% of his frxUSD
uint256 _maxAssetsUser = previewRedeem(frxUSD.balanceOf(address(_owner)));
// See how much custodianTkn is actually available in the contract
uint256 _assetBalanceContract = custodianTkn.balanceOf(address(this));
// Return the lesser of the two
_maxAssetsOut = ((_assetBalanceContract > _maxAssetsUser) ? _maxAssetsUser : _assetBalanceContract);
}
/// @notice Returns the maximum amount of shares that can be redeemed from the owner balance in the contract, through a redeem call. Includes fee.
/// @param _owner The address to check
/// @return _maxSharesIn The maximum amount of shares that can be redeemed
/**
* @dev See {IERC4626-maxRedeem}
* Lesser of
* a) User frxUSD
* b) Contract custodianTkn -> frxUSD amount
*/
function maxRedeem(address _owner) public view returns (uint256 _maxSharesIn) {
// See how much frxUSD the contract could honor if 100% of its custodianTkn was redeemed
uint256 _maxSharesContract = previewWithdraw(custodianTkn.balanceOf(address(this)));
// See how much frxUSD the user has
uint256 _sharesBalanceUser = frxUSD.balanceOf(address(_owner));
// Return the lesser of the two
_maxSharesIn = ((_maxSharesContract > _sharesBalanceUser) ? _sharesBalanceUser : _maxSharesContract);
}
/// @notice Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given current on-chain conditions.
/// @param _assetsIn Amount of underlying you want to deposit
/// @return _sharesOut The amount of output shares expected
/// @dev See {IERC4626-previewDeposit}
function previewDeposit(uint256 _assetsIn) public view returns (uint256 _sharesOut) {
uint256 fee = mintFee;
if (fee > 0) _assetsIn = Math.mulDiv(_assetsIn, (1e18 - fee), 1e18, Math.Rounding.Floor);
_sharesOut = _convertToShares(_assetsIn, Math.Rounding.Floor);
}
/// @notice Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given current on-chain conditions.
/// @param _sharesOut Amount of shares you want to mint
/// @return _assetsIn The amount of input assets needed
/// @dev See {IERC4626-previewMint}
function previewMint(uint256 _sharesOut) public view returns (uint256 _assetsIn) {
uint256 fee = mintFee;
_assetsIn = _convertToAssets(_sharesOut, Math.Rounding.Ceil);
if (fee > 0) _assetsIn = Math.mulDiv(_assetsIn, 1e18, (1e18 - fee), Math.Rounding.Ceil);
}
/// @notice Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block, given current on-chain conditions.
/// @param _assetsOut Amount of underlying tokens you want to get back
/// @return _sharesIn Amount of shares needed
/// @dev See {IERC4626-previewWithdraw}
function previewWithdraw(uint256 _assetsOut) public view returns (uint256 _sharesIn) {
uint256 fee = redeemFee;
if (fee > 0) _assetsOut = Math.mulDiv(_assetsOut, 1e18, (1e18 - fee), Math.Rounding.Ceil);
_sharesIn = _convertToShares(_assetsOut, Math.Rounding.Ceil);
}
/// @notice Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block, given current on-chain conditions.
/// @param _sharesIn Amount of shares you want to redeem
/// @return _assetsOut Amount of output asset expected
/// @dev See {IERC4626-previewRedeem}
function previewRedeem(uint256 _sharesIn) public view returns (uint256 _assetsOut) {
uint256 fee = redeemFee;
_assetsOut = _convertToAssets(_sharesIn, Math.Rounding.Floor);
if (fee > 0) _assetsOut = Math.mulDiv((1e18 - fee), _assetsOut, 1e18, Math.Rounding.Floor);
}
// ERC4626 INTERNAL VIEWS
// ===================================================
/// @dev Internal conversion function (from assets to shares) with support for rounding direction.
/// @param _assets Amount of underlying tokens to convert to shares
/// @param _rounding Math.Rounding rounding direction
/// @return _shares Amount of shares represented by the given underlying tokens
function _convertToShares(uint256 _assets, Math.Rounding _rounding) internal view virtual returns (uint256 _shares) {
_shares = Math.mulDiv(_assets, uint256(10 ** frxUSDDecimals), uint256(10 ** custodianTknDecimals), _rounding);
}
/// @dev Internal conversion function (from shares to assets) with support for rounding direction
/// @param _shares Amount of shares to convert to underlying tokens
/// @param _rounding Math.Rounding rounding direction
/// @return _assets Amount of underlying tokens represented by the given number of shares
function _convertToAssets(uint256 _shares, Math.Rounding _rounding) internal view virtual returns (uint256 _assets) {
_assets = Math.mulDiv(_shares, uint256(10 ** custodianTknDecimals), uint256(10 ** frxUSDDecimals), _rounding);
}
/// @notice Price of 1E18 shares, in asset tokens
/// @return _pricePerShare How many underlying asset tokens per 1E18 shares
function pricePerShare() external view returns (uint256 _pricePerShare) {
_pricePerShare = _convertToAssets(1e18, Math.Rounding.Floor);
}
// ADDITIONAL PUBLIC VIEWS
// ===================================================
/// @notice Helper view for max deposit, mint, withdraw, and redeem inputs
/// @return _maxAssetsDepositable Max amount of underlying asset you can deposit
/// @return _maxSharesMintable Max number of shares that can be minted
/// @return _maxAssetsWithdrawable Max amount of underlying asset withdrawable
/// @return _maxSharesRedeemable Max number of shares redeemable
function mdwrComboView()
public
view
returns (
uint256 _maxAssetsDepositable,
uint256 _maxSharesMintable,
uint256 _maxAssetsWithdrawable,
uint256 _maxSharesRedeemable
)
{
uint256 _maxMint = maxMint(address(this));
return (
previewMint(_maxMint),
_maxMint,
custodianTkn.balanceOf(address(this)),
previewWithdraw(custodianTkn.balanceOf(address(this)))
);
}
// ERC4626 INTERNAL MUTATORS
// ===================================================
/// @notice Deposit/mint common workflow.
/// @param _caller The caller
/// @param _receiver Reciever of the shares
/// @param _assets Amount of assets taken in
/// @param _shares Amount of shares given out
function _deposit(address _caller, address _receiver, uint256 _assets, uint256 _shares) internal nonReentrant {
// If _asset is ERC-777, `transferFrom` can trigger a reentrancy BEFORE the transfer happens through the
// `tokensToSend` hook. On the other hand, the `tokenReceived` hook, that is triggered after the transfer,
// calls the vault, which is assumed not malicious.
//
// Conclusion: we need to do the transfer beforehand so that any reentrancy would happen before the
// _assets are transferred and before the _shares are minted, which is a valid state.
// slither-disable-next-line reentrancy-no-eth
// Take in the assets
// User will need to approve _caller -> address(this) first
SafeERC20.safeTransferFrom(IERC20(address(custodianTkn)), _caller, address(this), _assets);
// Transfer out the shares / mint frxUSD
frxUSD.minter_mint(_receiver, _shares);
// frxUSD minted accounting
frxUSDMinted += _shares;
if (frxUSDMinted > mintCap) revert MintCapExceeded(_receiver, _shares, mintCap);
emit Deposit(_caller, _receiver, _assets, _shares);
}
/// @notice Withdraw/redeem common workflow.
/// @param _caller The caller
/// @param _receiver Reciever of the assets
/// @param _owner The owner of the shares
/// @param _assets Amount of assets given out
/// @param _shares Amount of shares taken in
function _withdraw(
address _caller,
address _receiver,
address _owner,
uint256 _assets,
uint256 _shares
) internal nonReentrant {
// If _asset is ERC-777, `transfer` can trigger a reentrancy AFTER the transfer happens through the
// `tokensReceived` hook. On the other hand, the `tokensToSend` hook, that is triggered before the transfer,
// calls the vault, which is assumed not malicious.
//
// Conclusion: we need to do the transfer afterwards so that any reentrancy would happen after the
// _shares are burned and after the _assets are transferred, which is a valid state.
// Take in the shares / burn frxUSD
// User will need to approve owner -> address(this) first
frxUSD.minter_burn_from(_caller, _shares);
// frxUSD minted accounting
if (frxUSDMinted < _shares) frxUSDMinted = 0;
else frxUSDMinted -= _shares;
// Transfer out the assets
SafeERC20.safeTransfer(IERC20(address(custodianTkn)), _receiver, _assets);
emit Withdraw(_caller, _receiver, _owner, _assets, _shares);
}
// ERC4626 PUBLIC/EXTERNAL MUTATIVE
// ===================================================
/// @notice Deposit a specified amount of underlying tokens and generate shares. Make sure to approve msg.sender's assets to this contract first.
/// @param _assetsIn Amount of underlying tokens you are depositing
/// @param _receiver Recipient of the generated shares
/// @return _sharesOut Amount of shares generated by the deposit
/// @dev See {IERC4626-deposit}
function deposit(uint256 _assetsIn, address _receiver) public virtual returns (uint256 _sharesOut) {
// See how many asset tokens the user can deposit
uint256 _maxAssets = maxDeposit(_receiver);
// Revert if the user is trying to deposit too many asset tokens
if (_assetsIn > _maxAssets) {
revert ERC4626ExceededMaxDeposit(_receiver, _assetsIn, _maxAssets);
}
// See how many shares would be generated with the specified number of asset tokens
_sharesOut = previewDeposit(_assetsIn);
// Do the deposit
_deposit(msg.sender, _receiver, _assetsIn, _sharesOut);
}
/// @notice Mint a specified amount of shares using underlying asset tokens. Make sure to approve msg.sender's assets to this contract first.
/// @param _sharesOut Amount of shares you want to mint
/// @param _receiver Recipient of the minted shares
/// @return _assetsIn Amount of assets used to generate the shares
/// @dev See {IERC4626-mint}
function mint(uint256 _sharesOut, address _receiver) public virtual returns (uint256 _assetsIn) {
// See how many shares the user's can mint
uint256 _maxShares = maxMint(_receiver);
// Revert if you are trying to mint too many shares
if (_sharesOut > _maxShares) {
revert ERC4626ExceededMaxMint(_receiver, _sharesOut, _maxShares);
}
// See how many asset tokens are needed to generate the specified amount of shares
_assetsIn = previewMint(_sharesOut);
// Do the minting
_deposit(msg.sender, _receiver, _assetsIn, _sharesOut);
}
/// @notice Withdraw a specified amount of underlying tokens. Make sure to approve _owner's shares to this contract first
/// @param _assetsOut Amount of asset tokens you want to withdraw
/// @param _receiver Recipient of the asset tokens
/// @param _owner Owner of the shares. Must be msg.sender
/// @return _sharesIn Amount of shares used for the withdrawal
/// @dev See {IERC4626-withdraw}. Leaving _owner param for ABI compatibility
function withdraw(uint256 _assetsOut, address _receiver, address _owner) public virtual returns (uint256 _sharesIn) {
// Make sure _owner is msg.sender
if (_owner != msg.sender) revert TokenOwnerShouldBeSender();
// See how much assets the owner can withdraw
uint256 _maxAssets = maxWithdraw(_owner);
// Revert if you are trying to withdraw too many asset tokens
if (_assetsOut > _maxAssets) {
revert ERC4626ExceededMaxWithdraw(_owner, _assetsOut, _maxAssets);
}
// See how many shares are needed
_sharesIn = previewWithdraw(_assetsOut);
// Do the withdrawal
_withdraw(msg.sender, _receiver, _owner, _assetsOut, _sharesIn);
}
/// @notice Redeem a specified amount of shares for the underlying tokens. Make sure to approve _owner's shares to this contract first.
/// @param _sharesIn Number of shares to redeem
/// @param _receiver Recipient of the underlying asset tokens
/// @param _owner Owner of the shares being redeemed. Must be msg.sender.
/// @return _assetsOut Amount of underlying tokens out
/// @dev See {IERC4626-redeem}. Leaving _owner param for ABI compatibility
function redeem(uint256 _sharesIn, address _receiver, address _owner) public virtual returns (uint256 _assetsOut) {
// Make sure _owner is msg.sender
if (_owner != msg.sender) revert TokenOwnerShouldBeSender();
// See how many shares the owner can redeem
uint256 _maxShares = maxRedeem(_owner);
// Revert if you are trying to redeem too many shares
if (_sharesIn > _maxShares) {
revert ERC4626ExceededMaxRedeem(_owner, _sharesIn, _maxShares);
}
// See how many asset tokens are expected
_assetsOut = previewRedeem(_sharesIn);
// Do the redemption
_withdraw(msg.sender, _receiver, _owner, _assetsOut, _sharesIn);
}
// RESTRICTED FUNCTIONS
// ===================================================
/// @notice Set the fee for the contract on mint|deposit/redeem|withdraw flow
/// @param _mintFee The mint fee
/// @param _redeemFee The redeem fee
function setMintRedeemFee(uint256 _mintFee, uint _redeemFee) public onlyOwner {
require(_mintFee < 1e18 && _redeemFee < 1e18, "Fee must be a fraction of underlying");
mintFee = _mintFee;
redeemFee = _redeemFee;
}
/// @notice Added to support tokens
/// @param _tokenAddress The token to recover
/// @param _tokenAmount The amount to recover
function recoverERC20(address _tokenAddress, uint256 _tokenAmount) external onlyOwner {
// Only the owner address can ever receive the recovery withdrawal
SafeERC20.safeTransfer(IERC20(_tokenAddress), owner(), _tokenAmount);
emit RecoveredERC20(_tokenAddress, _tokenAmount);
}
/// @notice Set the mint cap for frxUSD minting
/// @param _mintCap The new mint cap
function setMintCap(uint256 _mintCap) public onlyOwner {
mintCap = _mintCap;
emit MintCapSet(_mintCap);
}
/// @notice Set the status for a custodian operator
/// @param _operator The address of the operator whose status is updated
/// @param _status The status for the operator
/// @dev only callable via `owner`
function setApprovedOperator(address _operator, bool _status) public onlyOwner {
isApprovedOperator[_operator] = _status;
emit OperatorStatusSet(_operator, _status);
}
/// @notice Set the `minAfterShuffle` variable
/// @param _minAfterShuffle The minimum amt of `custodianTkn` that must remain in the contract
/// after the call to `shuffleToRwa`
/// @dev only callable via `owner`
function setMinAfterShuffle(uint256 _minAfterShuffle) public onlyOwner {
emit MinAmountAfterShuffleSet(minAfterShuffle, _minAfterShuffle);
minAfterShuffle = _minAfterShuffle;
}
/// @notice Function that will take excess usdc and shuffle it into RWA's earning RFR
/// @param amount The amount of `custodianTkn`
/// @param minAmountRwaOut The minimum amount of rwa out
/// @param assetToShuffleTo Enum representing the asset we want to shuffle to
function shuffleToRwa(uint256 amount, uint256 minAmountRwaOut, uint8 assetToShuffleTo) public virtual {
IRWAIssuer rwaIssuer = IRWAIssuer(0x43415eB6ff9DB7E26A15b704e7A3eDCe97d31C4e);
address rwaCustodian = 0x5fbAa3A3B489199338fbD85F7E3D444dc0504F33;
address wtgxxCustodian = 0x860Cc723935FC9A15fF8b1A94237a711DFeF7857;
if (!isApprovedOperator[msg.sender]) revert NotOperator();
if (custodianTkn.balanceOf(address(this)) - amount < minAfterShuffle) revert AmountTooHigh();
if (assetToShuffleTo == 0) {
uint256 totalAssetsStart;
if (minAmountRwaOut > 0) {
totalAssetsStart = FrxUSDCustodian(rwaCustodian).totalAssets();
}
custodianTkn.approve(address(rwaIssuer), amount);
rwaIssuer.subscribe(rwaCustodian, amount, address(custodianTkn));
if (minAmountRwaOut > 0) {
if (FrxUSDCustodian(rwaCustodian).totalAssets() - totalAssetsStart < minAmountRwaOut) {
revert SlippageTooHigh();
}
}
} else {
custodianTkn.transfer(wtgxxCustodian, amount);
IWtgxxCustodian(wtgxxCustodian).shuffleToWtgxx(amount);
}
}
/// @notice Extend the legacy abi of the contract
function shuffleToRwa(uint256 amount, uint256 minAmountRwaOut) external virtual {
shuffleToRwa(amount, minAmountRwaOut, 0);
}
// EVENTS
// ===================================================
/// @notice When a deposit/mint has occured
/// @param sender The transaction sender
/// @param owner The owner of the assets
/// @param assets Amount of assets taken in
/// @param shares Amount of shares given out
event Deposit(address indexed sender, address indexed owner, uint256 assets, uint256 shares);
/// @notice When ERC20 tokens were recovered
/// @param token Token address
/// @param amount Amount of tokens collected
event RecoveredERC20(address token, uint256 amount);
/// @notice When a withdrawal/redemption has occured
/// @param sender The transaction sender
/// @param receiver Reciever of the assets
/// @param owner The owner of the shares
/// @param assets Amount of assets given out
/// @param shares Amount of shares taken in
event Withdraw(
address indexed sender,
address indexed receiver,
address indexed owner,
uint256 assets,
uint256 shares
);
/// @notice When the mint cap is set
/// @param mintCap The new mint cap
event MintCapSet(uint256 mintCap);
/// @notice Event emitted when an operator is set
/// @param operator The address of the operator whose status is updated
/// @param status The status of the operator: true -> allowed | false -> disallowed
event OperatorStatusSet(address operator, bool status);
/// @notice Event emitted when the `minAfterShuffle` variable is set
/// @param oldValue The old `minAfterShuffle` value
/// @param newValue The new `minAfterShuffle` value
event MinAmountAfterShuffleSet(uint256 oldValue, uint256 newValue);
/// @notice Event emitted when `custodianTkn` is shuffled to RWA
/// @param custodianTknAmount The amount of `custodianTkn` shuffled
/// @param targetRwa The RWA Address we are shuffling to
event custodianTknShuffledToRwa(uint256 custodianTknAmount, address targetRwa);
// ERRORS
// ===================================================
/// @notice Attempted to deposit more assets than the max amount for `receiver`
/// @param receiver The intended recipient of the shares
/// @param assets The amount of underlying that was attempted to be deposited
/// @param max Max amount of underlying depositable
error ERC4626ExceededMaxDeposit(address receiver, uint256 assets, uint256 max);
/// @notice Attempted to mint more shares than the mint cap
/// @param receiver The intended recipient of the shares
/// @param shares The number of shares that was attempted to be minted
/// @param mintCap The mint cap
error MintCapExceeded(address receiver, uint256 shares, uint256 mintCap);
/// @notice Attempted to mint more shares than the max amount for `receiver`
/// @param receiver The intended recipient of the shares
/// @param shares The number of shares that was attempted to be minted
/// @param max Max number of shares mintable
error ERC4626ExceededMaxMint(address receiver, uint256 shares, uint256 max);
/// @notice Attempted to withdraw more assets than the max amount for `receiver`
/// @param owner The owner of the shares
/// @param assets The amount of underlying that was attempted to be withdrawn
/// @param max Max amount of underlying withdrawable
error ERC4626ExceededMaxWithdraw(address owner, uint256 assets, uint256 max);
/// @notice Attempted to redeem more shares than the max amount for `receiver`
/// @param owner The owner of the shares
/// @param shares The number of shares that was attempted to be redeemed
/// @param max Max number of shares redeemable
error ERC4626ExceededMaxRedeem(address owner, uint256 shares, uint256 max);
/// @notice Cannot initialize twice
error InitializeFailed();
/// @notice When you are attempting to pull tokens from an owner address that is not msg.sender
error TokenOwnerShouldBeSender();
/// @notice When msg.sender does not have operator status
error NotOperator();
/// @notice When the amount to convert to RWA's is too high
error AmountTooHigh();
/// @notice When rwa Subscription results in too little out
error SlippageTooHigh();
/// @notice When owner is set to address(0) in initialize
error OwnerCannotInitToZeroAddress();
}
"
},
"node_modules/@openzeppelin/contracts/token/ERC20/IERC20.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC-20 standard as defined in the ERC.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 value) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 value) external returns (bool);
}
"
},
"node_modules/@openzeppelin/contracts/token/ERC20/ERC20.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/ERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "./IERC20.sol";
import {IERC20Metadata} from "./extensions/IERC20Metadata.sol";
import {Context} from "../../utils/Context.sol";
import {IERC20Errors} from "../../interfaces/draft-IERC6093.sol";
/**
* @dev Implementation of the {IERC20} interface.
*
* This implementation is agnostic to the way tokens are created. This means
* that a supply mechanism has to be added in a derived contract using {_mint}.
*
* TIP: For a detailed writeup see our guide
* https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
* to implement supply mechanisms].
*
* The default value of {decimals} is 18. To change this, you should override
* this function so it returns a different value.
*
* We have followed general OpenZeppelin Contracts guidelines: functions revert
* instead returning `false` on failure. This behavior is nonetheless
* conventional and does not conflict with the expectations of ERC-20
* applications.
*/
abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors {
mapping(address account => uint256) private _balances;
mapping(address account => mapping(address spender => uint256)) private _allowances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
/**
* @dev Sets the values for {name} and {symbol}.
*
* All two of these values are immutable: they can only be set once during
* construction.
*/
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
/**
* @dev Returns the name of the token.
*/
function name() public view virtual returns (string memory) {
return _name;
}
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() public view virtual returns (string memory) {
return _symbol;
}
/**
* @dev Returns the number of decimals used to get its user representation.
* For example, if `decimals` equals `2`, a balance of `505` tokens should
* be displayed to a user as `5.05` (`505 / 10 ** 2`).
*
* Tokens usually opt for a value of 18, imitating the relationship between
* Ether and Wei. This is the default value returned by this function, unless
* it's overridden.
*
* NOTE: This information is only used for _display_ purposes: it in
* no way affects any of the arithmetic of the contract, including
* {IERC20-balanceOf} and {IERC20-transfer}.
*/
function decimals() public view virtual returns (uint8) {
return 18;
}
/**
* @dev See {IERC20-totalSupply}.
*/
function totalSupply() public view virtual returns (uint256) {
return _totalSupply;
}
/**
* @dev See {IERC20-balanceOf}.
*/
function balanceOf(address account) public view virtual returns (uint256) {
return _balances[account];
}
/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - the caller must have a balance of at least `value`.
*/
function transfer(address to, uint256 value) public virtual returns (bool) {
address owner = _msgSender();
_transfer(owner, to, value);
return true;
}
/**
* @dev See {IERC20-allowance}.
*/
function allowance(address owner, address spender) public view virtual returns (uint256) {
return _allowances[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*
* NOTE: If `value` is the maximum `uint256`, the allowance is not updated on
* `transferFrom`. This is semantically equivalent to an infinite approval.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function approve(address spender, uint256 value) public virtual returns (bool) {
address owner = _msgSender();
_approve(owner, spender, value);
return true;
}
/**
* @dev See {IERC20-transferFrom}.
*
* Skips emitting an {Approval} event indicating an allowance update. This is not
* required by the ERC. See {xref-ERC20-_approve-address-address-uint256-bool-}[_approve].
*
* NOTE: Does not update the allowance if the current allowance
* is the maximum `uint256`.
*
* Requirements:
*
* - `from` and `to` cannot be the zero address.
* - `from` must have a balance of at least `value`.
* - the caller must have allowance for ``from``'s tokens of at least
* `value`.
*/
function transferFrom(address from, address to, uint256 value) public virtual returns (bool) {
address spender = _msgSender();
_spendAllowance(from, spender, value);
_transfer(from, to, value);
return true;
}
/**
* @dev Moves a `value` amount of tokens from `from` to `to`.
*
* This internal function is equivalent to {transfer}, and can be used to
* e.g. implement automatic token fees, slashing mechanisms, etc.
*
* Emits a {Transfer} event.
*
* NOTE: This function is not virtual, {_update} should be overridden instead.
*/
function _transfer(address from, address to, uint256 value) internal {
if (from == address(0)) {
revert ERC20InvalidSender(address(0));
}
if (to == address(0)) {
revert ERC20InvalidReceiver(address(0));
}
_update(from, to, value);
}
/**
* @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from`
* (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding
* this function.
*
* Emits a {Transfer} event.
*/
function _update(address from, address to, uint256 value) internal virtual {
if (from == address(0)) {
// Overflow check required: The rest of the code assumes that totalSupply never overflows
_totalSupply += value;
} else {
uint256 fromBalance = _balances[from];
if (fromBalance < value) {
revert ERC20InsufficientBalance(from, fromBalance, value);
}
unchecked {
// Overflow not possible: value <= fromBalance <= totalSupply.
_balances[from] = fromBalance - value;
}
}
if (to == address(0)) {
unchecked {
// Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply.
_totalSupply -= value;
}
} else {
unchecked {
// Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256.
_balances[to] += value;
}
}
emit Transfer(from, to, value);
}
/**
* @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0).
* Relies on the `_update` mechanism
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* NOTE: This function is not virtual, {_update} should be overridden instead.
*/
function _mint(address account, uint256 value) internal {
if (account == address(0)) {
revert ERC20InvalidReceiver(address(0));
}
_update(address(0), account, value);
}
/**
* @dev Destroys a `value` amount of tokens from `account`, lowering the total supply.
* Relies on the `_update` mechanism.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* NOTE: This function is not virtual, {_update} should be overridden instead
*/
function _burn(address account, uint256 value) internal {
if (account == address(0)) {
revert ERC20InvalidSender(address(0));
}
_update(account, address(0), value);
}
/**
* @dev Sets `value` as the allowance of `spender` over the `owner` s tokens.
*
* This internal function is equivalent to `approve`, and can be used to
* e.g. set automatic allowances for certain subsystems, etc.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
*
* Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument.
*/
function _approve(address owner, address spender, uint256 value) internal {
_approve(owner, spender, value, true);
}
/**
* @dev Variant of {_approve} with an optional flag to enable or disable the {Approval} event.
*
* By default (when calling {_approve}) the flag is set to true. On the other hand, approval changes made by
* `_spendAllowance` during the `transferFrom` operation set the flag to false. This saves gas by not emitting any
* `Approval` event during `transferFrom` operations.
*
* Anyone who wishes to continue emitting `Approval` events on the`transferFrom` operation can force the flag to
* true using the following override:
*
* ```solidity
* function _approve(address owner, address spender, uint256 value, bool) internal virtual override {
* super._approve(owner, spender, value, true);
* }
* ```
*
* Requirements are the same as {_approve}.
*/
function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual {
if (owner == address(0)) {
revert ERC20InvalidApprover(address(0));
}
if (spender == address(0)) {
revert ERC20InvalidSpender(address(0));
}
_allowances[owner][spender] = value;
if (emitEvent) {
emit Approval(owner, spender, value);
}
}
/**
* @dev Updates `owner` s allowance for `spender` based on spent `value`.
*
* Does not update the allowance value in case of infinite allowance.
* Revert if not enough allowance is available.
*
* Does not emit an {Approval} event.
*/
function _spendAllowance(address owner, address spender, uint256 value) internal virtual {
uint256 currentAllowance = allowance(owner, spender);
if (currentAllowance != type(uint256).max) {
if (currentAllowance < value) {
revert ERC20InsufficientAllowance(spender, currentAllowance, value);
}
unchecked {
_approve(owner, spender, currentAllowance - value, false);
}
}
}
}
"
},
"node_modules/@openzeppelin/contracts/access/Ownable2Step.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (access/Ownable2Step.sol)
pragma solidity ^0.8.20;
import {Ownable} from "./Ownable.sol";
/**
* @dev Contract module which provides access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* This extension of the {Ownable} contract includes a two-step mechanism to transfer
* ownership, where the new owner must call {acceptOwnership} in order to replace the
* old one. This can help prevent common mistakes, such as transfers of ownership to
* incorrect accounts, or to contracts that are unable to interact with the
* permission system.
*
* The initial owner is specified at deployment time in the constructor for `Ownable`. This
* can later be changed with {transferOwnership} and {acceptOwnership}.
*
* This module is used through inheritance. It will make available all functions
* from parent (Ownable).
*/
abstract contract Ownable2Step is Ownable {
address private _pendingOwner;
event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);
/**
* @dev Returns the address of the pending owner.
*/
function pendingOwner() public view virtual returns (address) {
return _pendingOwner;
}
/**
* @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.
* Can only be called by the current owner.
*
* Setting `newOwner` to the zero address is allowed; this can be used to cancel an initiated ownership transfer.
*/
function transferOwnership(address newOwner) public virtual override onlyOwner {
_pendingOwner = newOwner;
emit OwnershipTransferStarted(owner(), newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual override {
delete _pendingOwner;
super._transferOwnership(newOwner);
}
/**
* @dev The new owner accepts the ownership transfer.
*/
function acceptOwnership() public virtual {
address sender = _msgSender();
if (pendingOwner() != sender) {
revert OwnableUnauthorizedAccount(sender);
}
_transferOwnership(sender);
}
}
"
},
"node_modules/@openzeppelin/contracts/access/Ownable.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
pragma solidity ^0.8.20;
import {Context} from "../utils/Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* The initial owner is set to the address provided by the deployer. This can
* later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract Ownable is Context {
address private _owner;
/**
* @dev The caller account is not authorized to perform an operation.
*/
error OwnableUnauthorizedAccount(address account);
/**
* @dev The owner is not a valid owner account. (eg. `address(0)`)
*/
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the address provided by the deployer as the initial owner.
*/
constructor(address initialOwner) {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
"
},
"node_modules/@openzeppelin/contracts/utils/math/Math.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/math/Math.sol)
pragma solidity ^0.8.20;
import {Panic} from "../Panic.sol";
import {SafeCast} from "./SafeCast.sol";
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
enum Rounding {
Floor, // Toward negative infinity
Ceil, // Toward positive infinity
Trunc, // Toward zero
Expand // Away from zero
}
/**
* @dev Returns the addition of two unsigned integers, with an success flag (no overflow).
*/
function tryAdd(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the subtraction of two unsigned integers, with an success flag (no overflow).
*/
function trySub(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
if (b > a) return (false, 0);
return (true, a - b);
}
}
/**
* @dev Returns the multiplication of two unsigned integers, with an success flag (no overflow).
*/
function tryMul(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the division of two unsigned integers, with a success flag (no division by zero).
*/
function tryDiv(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
if (b == 0) return (false, 0);
return (true, a / b);
}
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a success flag (no division by zero).
*/
function tryMod(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
if (b == 0) return (false, 0);
return (true, a % b);
}
}
/**
* @dev Branchless ternary evaluation for `a ? b : c`. Gas costs are constant.
*
* IMPORTANT: This function may reduce bytecode size and consume less gas when used standalone.
* However, the compiler may optimize Solidity ternary operations (i.e. `a ? b : c`) to only compute
* one branch when needed, making this function more expensive.
*/
function ternary(bool condition, uint256 a, uint256 b) internal pure returns (uint256) {
unchecked {
// branchless ternary works because:
// b ^ (a ^ b) == a
// b ^ 0 == b
return b ^ ((a ^ b) * SafeCast.toUint(condition));
}
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return ternary(a > b, a, b);
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return ternary(a < b, a, b);
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds towards infinity instead
* of rounding towards zero.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
if (b == 0) {
// Guarantee the same behavior as in a regular Solidity division.
Panic.panic(Panic.DIVISION_BY_ZERO);
}
// The following calculation ensures accurate ceiling division without overflow.
// Since a is non-zero, (a - 1) / b will not overflow.
// The largest possible result occurs when (a - 1) / b is type(uint256).max,
// but the largest value we can obtain is type(uint256).max - 1, which happens
// when a = type(uint256).max and b = 1.
unchecked {
return SafeCast.toUint(a > 0) * ((a - 1) / b + 1);
}
}
/**
* @dev Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
* denominator == 0.
*
* Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
* Uniswap Labs also under MIT license.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2²⁵⁶ and mod 2²⁵⁶ - 1, then use
// the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2²⁵⁶ + prod0.
uint256 prod0 = x * y; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
// Solidity will revert if denominator == 0, unlike the div opcode on its own.
// The surrounding unchecked block does not change this fact.
// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
return prod0 / denominator;
}
// Make sure the result is less than 2²⁵⁶. Also prevents denominator == 0.
if (denominator <= prod1) {
Panic.panic(ternary(denominator == 0, Panic.DIVISION_BY_ZERO, Panic.UNDER_OVERFLOW));
}
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator.
// Always >= 1. See https://cs.stackexchange.com/q/138556/92363.
uint256 twos = denominator & (0 - denominator);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2²⁵⁶ / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2²⁵⁶. Now that denominator is an odd number, it has an inverse modulo 2²⁵⁶ such
// that denominator * inv ≡ 1 mod 2²⁵⁶. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv ≡ 1 mod 2⁴.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also
// works in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2⁸
inverse *= 2 - denominator * inverse; // inverse mod 2¹⁶
inverse *= 2 - denominator * inverse; // inverse mod 2³²
inverse *= 2 - denominator * inverse; // inverse mod 2⁶⁴
inverse *= 2 - denominator * inverse; // inverse mod 2¹²⁸
inverse *= 2 - denominator * inverse; // inverse mod 2²⁵⁶
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2²⁵⁶. Since the preconditions guarantee that the outcome is
// less than 2²⁵⁶, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @dev Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
return mulDiv(x, y, denominator) + SafeCast.toUint(unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0);
}
/**
* @dev Calculate the modular multiplicative inverse of a number in Z/nZ.
*
* If n is a prime, then Z/nZ is a field. In that case all elements are inversible, except 0.
* If n is not a prime, then Z/nZ is not a field, and some elements might not be inversible.
*
* If the input value is not inversible, 0 is returned.
*
* NOTE: If you know for sure that n is (big) a prime, it may be cheaper to use Fermat's little theorem and get the
* inverse using `Math.modExp(a, n - 2, n)`. See {invModPrime}.
*/
function invMod(uint256 a, uint256 n) internal pure returns (uint256) {
unchecked {
if (n == 0) return 0;
// The inverse modulo is calculated using the Extended Euclidean Algorithm (iterative version)
// Used to compute integers x and y such that: ax + ny = gcd(a, n).
// When the gcd is 1, then the inverse of a modulo n exists and it's x.
// ax + ny = 1
// ax = 1 + (-y)n
// ax ≡ 1 (mod n) # x is the inverse of a modulo n
// If the remainder is 0 the gcd is n right away.
uint256 remainder = a % n;
uint256 gcd = n;
// Therefore the initial coefficients are:
// ax + ny = gcd(a, n) = n
// 0a + 1n = n
int256 x = 0;
int256 y = 1;
while (remainder != 0) {
uint256 quotient = gcd / remainder;
(gcd, remainder) = (
// The old remainder is the next gcd to try.
remainder,
// Compute the next remainder.
// Can't overflow given that (a % gcd) * (gcd // (a % gcd)) <= gcd
// where gcd is at most n (capped to type(uint256).max)
gcd - remainder * quotient
);
(x, y) = (
// Increment the coefficient of a.
y,
// Decrement the coefficient of n.
// Can overflow, but the result is casted to uint256 so that the
// next value of y is "wrapped around" to a value between 0 and n - 1.
x - y * int256(quotient)
);
}
if (gcd != 1) return 0; // No inverse exists.
return ternary(x < 0, n - uint256(-x), uint256(x)); // Wrap the result if it's negative.
}
}
/**
* @dev Variant of {invMod}. More efficient, but only works if `p` is known to be a prime greater than `2`.
*
* From https://en.wikipedia.org/wiki/Fermat%27s_little_theorem[Fermat's little theorem], we know that if p is
* prime, then `a**(p-1) ≡ 1 mod p`. As a consequence, we have `a * a**(p-2) ≡ 1 mod p`, which means that
* `a**(p-2)` is the modular multiplicative inverse of a in Fp.
*
* NOTE: this function does N
Submitted on: 2025-10-16 21:00:19
Comments
Log in to comment.
No comments yet.