//SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.13;
import "@equilibria/root/number/types/UFixed18.sol";
import "@equilibria/root/token/types/Token18.sol";
import "@equilibria/root/token/types/Token6.sol";
import "@equilibria/root/control/unstructured/UOwnable.sol";
import "../interfaces/IBatcher.sol";
import "../interfaces/IEmptySetReserve.sol";
abstract contract Batcher is IBatcher, UOwnable {
/// @dev Reserve address
IEmptySetReserve public immutable RESERVE; // solhint-disable-line var-name-mixedcase
/// @dev DSU address
Token18 public immutable DSU; // solhint-disable-line var-name-mixedcase
/// @dev USDC address
Token6 public immutable USDC; // solhint-disable-line var-name-mixedcase
/**
* @notice Initializes the Batcher
* @dev Called at implementation instantiate and constant for that implementation.
* @param reserve EmptySet Reserve Aaddress
* @param dsu DSU Token address
* @param usdc USDC Token Address
*/
constructor(IEmptySetReserve reserve, Token18 dsu, Token6 usdc) {
RESERVE = reserve;
DSU = dsu;
USDC = usdc;
DSU.approve(address(RESERVE));
USDC.approve(address(RESERVE));
__UOwnable__initialize();
}
/**
* @notice Total USDC and DSU balance of the Batcher
* @return Balance of DSU + balance of USDC
*/
function totalBalance() public view returns (UFixed18) {
return DSU.balanceOf().add(USDC.balanceOf());
}
/**
* @notice Wraps `amount` of USDC, returned DSU to `to`
* @param amount Amount of USDC to wrap
* @param to Receiving address of resulting DSU
*/
function wrap(UFixed18 amount, address to) external {
_wrap(amount, to);
emit Wrap(to, amount);
}
/**
* @notice Pulls USDC from the `msg.sender` and pushes DSU to `to`
* @dev Rounds USDC amount up if `amount` exceeds USDC decimal precision. Overrideable by implementation
* @param amount Amount of USDC to pull
* @param to Receiving address of resulting DSU
*/
function _wrap(UFixed18 amount, address to) virtual internal {
USDC.pull(msg.sender, amount, true);
DSU.push(to, amount);
}
/**
* @notice Unwraps `amount` of DSU, returned USDC to `to`
* @param amount Amount of DSU to unwrap
* @param to Receiving address of resulting USDC
*/
function unwrap(UFixed18 amount, address to) external {
_unwrap(amount, to);
emit Unwrap(to, amount);
}
/**
* @notice Pulls DSU from the `msg.sender` and pushes USDC to `to`
* @dev Rounds USDC amount down if `amount` exceeds USDC decimal precision. Overrideable by implementation
* @param amount Amount of DSU to pull
* @param to Receiving address of resulting USDC
*/
function _unwrap(UFixed18 amount, address to) virtual internal {
DSU.pull(msg.sender, amount);
USDC.push(to, amount);
}
/**
* @notice Rebalances the USDC and DSU in the Batcher to maintain target balances
* @dev Reverts if the new total balance is less than before
*/
function rebalance() public {
(UFixed18 usdcBalance, UFixed18 dsuBalance) = (USDC.balanceOf(), DSU.balanceOf());
_rebalance(usdcBalance, dsuBalance);
UFixed18 newDsuBalance = DSU.balanceOf();
(UFixed18 oldBalance, UFixed18 newBalance) = (usdcBalance.add(dsuBalance), totalBalance());
if (oldBalance.gt(newBalance)) revert BatcherBalanceMismatchError(oldBalance, newBalance);
emit Rebalance(
newDsuBalance.gt(dsuBalance) ? newDsuBalance.sub(dsuBalance) : UFixed18Lib.ZERO,
dsuBalance.gt(newDsuBalance) ? dsuBalance.sub(newDsuBalance) : UFixed18Lib.ZERO
);
}
/// @dev Hook for implementation for custom rebalance logic
function _rebalance(UFixed18 usdcBalance, UFixed18 dsuBalance) virtual internal;
/**
* @notice Closes the Batcher. Repaying debt to Reserve and returning excess USDC to owner.
*/
function close() external onlyOwner {
_close();
UFixed18 dsuBalance = DSU.balanceOf();
UFixed18 repayAmount = UFixed18Lib.min(RESERVE.debt(address(this)), dsuBalance);
UFixed18 returnAmount = dsuBalance.sub(repayAmount);
RESERVE.repay(address(this), repayAmount);
// If there is any excess DSU, redeem it for USDC and send to the owner
if (!returnAmount.isZero()) {
RESERVE.redeem(returnAmount);
USDC.push(owner(), returnAmount);
}
emit Close(dsuBalance);
}
/// @dev Hook for implementation for custom close logic
function _close() virtual internal;
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
pragma solidity ^0.8.0;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/ERC20.sol)
pragma solidity ^0.8.0;
import "./IERC20.sol";
import "./extensions/IERC20Metadata.sol";
import "../../utils/Context.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}.
* For a generic mechanism see {ERC20PresetMinterPauser}.
*
* TIP: For a detailed writeup see our guide
* https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
* to implement supply mechanisms].
*
* 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 ERC20
* applications.
*
* Additionally, an {Approval} event is emitted on calls to {transferFrom}.
* This allows applications to reconstruct the allowance for all accounts just
* by listening to said events. Other implementations of the EIP may not emit
* these events, as it isn't required by the specification.
*
* Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
* functions have been added to mitigate the well-known issues around setting
* allowances. See {IERC20-approve}.
*/
contract ERC20 is Context, IERC20, IERC20Metadata {
mapping(address => uint256) private _balances;
mapping(address => mapping(address => uint256)) private _allowances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
/**
* @dev Sets the values for {name} and {symbol}.
*
* The default value of {decimals} is 18. To select a different value for
* {decimals} you should overload it.
*
* 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 override returns (string memory) {
return _name;
}
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() public view virtual override 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 value {ERC20} uses, unless this function is
* 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 override returns (uint8) {
return 18;
}
/**
* @dev See {IERC20-totalSupply}.
*/
function totalSupply() public view virtual override returns (uint256) {
return _totalSupply;
}
/**
* @dev See {IERC20-balanceOf}.
*/
function balanceOf(address account) public view virtual override 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 `amount`.
*/
function transfer(address to, uint256 amount) public virtual override returns (bool) {
address owner = _msgSender();
_transfer(owner, to, amount);
return true;
}
/**
* @dev See {IERC20-allowance}.
*/
function allowance(address owner, address spender) public view virtual override returns (uint256) {
return _allowances[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*
* NOTE: If `amount` 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 amount) public virtual override returns (bool) {
address owner = _msgSender();
_approve(owner, spender, amount);
return true;
}
/**
* @dev See {IERC20-transferFrom}.
*
* Emits an {Approval} event indicating the updated allowance. This is not
* required by the EIP. See the note at the beginning of {ERC20}.
*
* 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 `amount`.
* - the caller must have allowance for ``from``'s tokens of at least
* `amount`.
*/
function transferFrom(
address from,
address to,
uint256 amount
) public virtual override returns (bool) {
address spender = _msgSender();
_spendAllowance(from, spender, amount);
_transfer(from, to, amount);
return true;
}
/**
* @dev Atomically increases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
address owner = _msgSender();
_approve(owner, spender, allowance(owner, spender) + addedValue);
return true;
}
/**
* @dev Atomically decreases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `spender` must have allowance for the caller of at least
* `subtractedValue`.
*/
function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
address owner = _msgSender();
uint256 currentAllowance = allowance(owner, spender);
require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
unchecked {
_approve(owner, spender, currentAllowance - subtractedValue);
}
return true;
}
/**
* @dev Moves `amount` of tokens from `sender` to `recipient`.
*
* 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.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `from` must have a balance of at least `amount`.
*/
function _transfer(
address from,
address to,
uint256 amount
) internal virtual {
require(from != address(0), "ERC20: transfer from the zero address");
require(to != address(0), "ERC20: transfer to the zero address");
_beforeTokenTransfer(from, to, amount);
uint256 fromBalance = _balances[from];
require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
unchecked {
_balances[from] = fromBalance - amount;
}
_balances[to] += amount;
emit Transfer(from, to, amount);
_afterTokenTransfer(from, to, amount);
}
/** @dev Creates `amount` tokens and assigns them to `account`, increasing
* the total supply.
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* Requirements:
*
* - `account` cannot be the zero address.
*/
function _mint(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: mint to the zero address");
_beforeTokenTransfer(address(0), account, amount);
_totalSupply += amount;
_balances[account] += amount;
emit Transfer(address(0), account, amount);
_afterTokenTransfer(address(0), account, amount);
}
/**
* @dev Destroys `amount` tokens from `account`, reducing the
* total supply.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* Requirements:
*
* - `account` cannot be the zero address.
* - `account` must have at least `amount` tokens.
*/
function _burn(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: burn from the zero address");
_beforeTokenTransfer(account, address(0), amount);
uint256 accountBalance = _balances[account];
require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
unchecked {
_balances[account] = accountBalance - amount;
}
_totalSupply -= amount;
emit Transfer(account, address(0), amount);
_afterTokenTransfer(account, address(0), amount);
}
/**
* @dev Sets `amount` 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.
*/
function _approve(
address owner,
address spender,
uint256 amount
) internal virtual {
require(owner != address(0), "ERC20: approve from the zero address");
require(spender != address(0), "ERC20: approve to the zero address");
_allowances[owner][spender] = amount;
emit Approval(owner, spender, amount);
}
/**
* @dev Updates `owner` s allowance for `spender` based on spent `amount`.
*
* Does not update the allowance amount in case of infinite allowance.
* Revert if not enough allowance is available.
*
* Might emit an {Approval} event.
*/
function _spendAllowance(
address owner,
address spender,
uint256 amount
) internal virtual {
uint256 currentAllowance = allowance(owner, spender);
if (currentAllowance != type(uint256).max) {
require(currentAllowance >= amount, "ERC20: insufficient allowance");
unchecked {
_approve(owner, spender, currentAllowance - amount);
}
}
}
/**
* @dev Hook that is called before any transfer of tokens. This includes
* minting and burning.
*
* Calling conditions:
*
* - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* will be transferred to `to`.
* - when `from` is zero, `amount` tokens will be minted for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens will be burned.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _beforeTokenTransfer(
address from,
address to,
uint256 amount
) internal virtual {}
/**
* @dev Hook that is called after any transfer of tokens. This includes
* minting and burning.
*
* Calling conditions:
*
* - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* has been transferred to `to`.
* - when `from` is zero, `amount` tokens have been minted for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens have been burned.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _afterTokenTransfer(
address from,
address to,
uint256 amount
) internal virtual {}
}
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.13;
import "@openzeppelin/contracts/utils/math/SignedMath.sol";
import "./UFixed18.sol";
import "./PackedFixed18.sol";
/// @dev Fixed18 type
type Fixed18 is int256;
using Fixed18Lib for Fixed18 global;
type Fixed18Storage is bytes32;
using Fixed18StorageLib for Fixed18Storage global;
/**
* @title Fixed18Lib
* @notice Library for the signed fixed-decimal type.
*/
library Fixed18Lib {
error Fixed18OverflowError(uint256 value);
error Fixed18PackingOverflowError(int256 value);
error Fixed18PackingUnderflowError(int256 value);
int256 private constant BASE = 1e18;
Fixed18 public constant ZERO = Fixed18.wrap(0);
Fixed18 public constant ONE = Fixed18.wrap(BASE);
Fixed18 public constant NEG_ONE = Fixed18.wrap(-1 * BASE);
Fixed18 public constant MAX = Fixed18.wrap(type(int256).max);
Fixed18 public constant MIN = Fixed18.wrap(type(int256).min);
/**
* @notice Creates a signed fixed-decimal from an unsigned fixed-decimal
* @param a Unsigned fixed-decimal
* @return New signed fixed-decimal
*/
function from(UFixed18 a) internal pure returns (Fixed18) {
uint256 value = UFixed18.unwrap(a);
if (value > uint256(type(int256).max)) revert Fixed18OverflowError(value);
return Fixed18.wrap(int256(value));
}
/**
* @notice Creates a signed fixed-decimal from a sign and an unsigned fixed-decimal
* @param s Sign
* @param m Unsigned fixed-decimal magnitude
* @return New signed fixed-decimal
*/
function from(int256 s, UFixed18 m) internal pure returns (Fixed18) {
if (s > 0) return from(m);
if (s < 0) return Fixed18.wrap(-1 * Fixed18.unwrap(from(m)));
return ZERO;
}
/**
* @notice Creates a signed fixed-decimal from a signed integer
* @param a Signed number
* @return New signed fixed-decimal
*/
function from(int256 a) internal pure returns (Fixed18) {
return Fixed18.wrap(a * BASE);
}
/**
* @notice Creates a packed signed fixed-decimal from an signed fixed-decimal
* @param a signed fixed-decimal
* @return New packed signed fixed-decimal
*/
function pack(Fixed18 a) internal pure returns (PackedFixed18) {
int256 value = Fixed18.unwrap(a);
if (value > type(int128).max) revert Fixed18PackingOverflowError(value);
if (value < type(int128).min) revert Fixed18PackingUnderflowError(value);
return PackedFixed18.wrap(int128(value));
}
/**
* @notice Returns whether the signed fixed-decimal is equal to zero.
* @param a Signed fixed-decimal
* @return Whether the signed fixed-decimal is zero.
*/
function isZero(Fixed18 a) internal pure returns (bool) {
return Fixed18.unwrap(a) == 0;
}
/**
* @notice Adds two signed fixed-decimals `a` and `b` together
* @param a First signed fixed-decimal
* @param b Second signed fixed-decimal
* @return Resulting summed signed fixed-decimal
*/
function add(Fixed18 a, Fixed18 b) internal pure returns (Fixed18) {
return Fixed18.wrap(Fixed18.unwrap(a) + Fixed18.unwrap(b));
}
/**
* @notice Subtracts signed fixed-decimal `b` from `a`
* @param a Signed fixed-decimal to subtract from
* @param b Signed fixed-decimal to subtract
* @return Resulting subtracted signed fixed-decimal
*/
function sub(Fixed18 a, Fixed18 b) internal pure returns (Fixed18) {
return Fixed18.wrap(Fixed18.unwrap(a) - Fixed18.unwrap(b));
}
/**
* @notice Multiplies two signed fixed-decimals `a` and `b` together
* @param a First signed fixed-decimal
* @param b Second signed fixed-decimal
* @return Resulting multiplied signed fixed-decimal
*/
function mul(Fixed18 a, Fixed18 b) internal pure returns (Fixed18) {
return Fixed18.wrap(Fixed18.unwrap(a) * Fixed18.unwrap(b) / BASE);
}
/**
* @notice Divides signed fixed-decimal `a` by `b`
* @param a Signed fixed-decimal to divide
* @param b Signed fixed-decimal to divide by
* @return Resulting divided signed fixed-decimal
*/
function div(Fixed18 a, Fixed18 b) internal pure returns (Fixed18) {
return Fixed18.wrap(Fixed18.unwrap(a) * BASE / Fixed18.unwrap(b));
}
/**
* @notice Divides unsigned fixed-decimal `a` by `b`
* @dev Does not revert on divide-by-0, instead returns `ONE` for `0/0`, `MAX` for `n/0`, and `MIN` for `-n/0`.
* @param a Unsigned fixed-decimal to divide
* @param b Unsigned fixed-decimal to divide by
* @return Resulting divided unsigned fixed-decimal
*/
function unsafeDiv(Fixed18 a, Fixed18 b) internal pure returns (Fixed18) {
if (isZero(b)) {
if (gt(a, ZERO)) return MAX;
if (lt(a, ZERO)) return MIN;
return ONE;
} else {
return div(a, b);
}
}
/**
* @notice Computes a * b / c without loss of precision due to BASE conversion
* @param a First signed fixed-decimal
* @param b Signed number to multiply by
* @param c Signed number to divide by
* @return Resulting computation
*/
function muldiv(Fixed18 a, int256 b, int256 c) internal pure returns (Fixed18) {
return muldiv(a, Fixed18.wrap(b), Fixed18.wrap(c));
}
/**
* @notice Computes a * b / c without loss of precision due to BASE conversion
* @param a First signed fixed-decimal
* @param b Signed fixed-decimal to multiply by
* @param c Signed fixed-decimal to divide by
* @return Resulting computation
*/
function muldiv(Fixed18 a, Fixed18 b, Fixed18 c) internal pure returns (Fixed18) {
return Fixed18.wrap(Fixed18.unwrap(a) * Fixed18.unwrap(b) / Fixed18.unwrap(c));
}
/**
* @notice Returns whether signed fixed-decimal `a` is equal to `b`
* @param a First signed fixed-decimal
* @param b Second signed fixed-decimal
* @return Whether `a` is equal to `b`
*/
function eq(Fixed18 a, Fixed18 b) internal pure returns (bool) {
return compare(a, b) == 1;
}
/**
* @notice Returns whether signed fixed-decimal `a` is greater than `b`
* @param a First signed fixed-decimal
* @param b Second signed fixed-decimal
* @return Whether `a` is greater than `b`
*/
function gt(Fixed18 a, Fixed18 b) internal pure returns (bool) {
return compare(a, b) == 2;
}
/**
* @notice Returns whether signed fixed-decimal `a` is less than `b`
* @param a First signed fixed-decimal
* @param b Second signed fixed-decimal
* @return Whether `a` is less than `b`
*/
function lt(Fixed18 a, Fixed18 b) internal pure returns (bool) {
return compare(a, b) == 0;
}
/**
* @notice Returns whether signed fixed-decimal `a` is greater than or equal to `b`
* @param a First signed fixed-decimal
* @param b Second signed fixed-decimal
* @return Whether `a` is greater than or equal to `b`
*/
function gte(Fixed18 a, Fixed18 b) internal pure returns (bool) {
return gt(a, b) || eq(a, b);
}
/**
* @notice Returns whether signed fixed-decimal `a` is less than or equal to `b`
* @param a First signed fixed-decimal
* @param b Second signed fixed-decimal
* @return Whether `a` is less than or equal to `b`
*/
function lte(Fixed18 a, Fixed18 b) internal pure returns (bool) {
return lt(a, b) || eq(a, b);
}
/**
* @notice Compares the signed fixed-decimals `a` and `b`
* @dev Returns: 2 for greater than
* 1 for equal to
* 0 for less than
* @param a First signed fixed-decimal
* @param b Second signed fixed-decimal
* @return Compare result of `a` and `b`
*/
function compare(Fixed18 a, Fixed18 b) internal pure returns (uint256) {
(int256 au, int256 bu) = (Fixed18.unwrap(a), Fixed18.unwrap(b));
if (au > bu) return 2;
if (au < bu) return 0;
return 1;
}
/**
* @notice Returns a signed fixed-decimal representing the ratio of `a` over `b`
* @param a First signed number
* @param b Second signed number
* @return Ratio of `a` over `b`
*/
function ratio(int256 a, int256 b) internal pure returns (Fixed18) {
return Fixed18.wrap(a * BASE / b);
}
/**
* @notice Returns the minimum of signed fixed-decimals `a` and `b`
* @param a First signed fixed-decimal
* @param b Second signed fixed-decimal
* @return Minimum of `a` and `b`
*/
function min(Fixed18 a, Fixed18 b) internal pure returns (Fixed18) {
return Fixed18.wrap(SignedMath.min(Fixed18.unwrap(a), Fixed18.unwrap(b)));
}
/**
* @notice Returns the maximum of signed fixed-decimals `a` and `b`
* @param a First signed fixed-decimal
* @param b Second signed fixed-decimal
* @return Maximum of `a` and `b`
*/
function max(Fixed18 a, Fixed18 b) internal pure returns (Fixed18) {
return Fixed18.wrap(SignedMath.max(Fixed18.unwrap(a), Fixed18.unwrap(b)));
}
/**
* @notice Converts the signed fixed-decimal into an integer, truncating any decimal portion
* @param a Signed fixed-decimal
* @return Truncated signed number
*/
function truncate(Fixed18 a) internal pure returns (int256) {
return Fixed18.unwrap(a) / BASE;
}
/**
* @notice Returns the sign of the signed fixed-decimal
* @dev Returns: -1 for negative
* 0 for zero
* 1 for positive
* @param a Signed fixed-decimal
* @return Sign of the signed fixed-decimal
*/
function sign(Fixed18 a) internal pure returns (int256) {
if (Fixed18.unwrap(a) > 0) return 1;
if (Fixed18.unwrap(a) < 0) return -1;
return 0;
}
/**
* @notice Returns the absolute value of the signed fixed-decimal
* @param a Signed fixed-decimal
* @return Absolute value of the signed fixed-decimal
*/
function abs(Fixed18 a) internal pure returns (UFixed18) {
return UFixed18.wrap(SignedMath.abs(Fixed18.unwrap(a)));
}
}
library Fixed18StorageLib {
function read(Fixed18Storage self) internal view returns (Fixed18 value) {
assembly {
value := sload(self)
}
}
function store(Fixed18Storage self, Fixed18 value) internal {
assembly {
sstore(self, value)
}
}
}
//SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.13;
import "@equilibria/root/number/types/UFixed18.sol";
import "@equilibria/root/token/types/Token18.sol";
import "@equilibria/root/token/types/Token6.sol";
import "../interfaces/IEmptySetReserve.sol";
interface IBatcher {
event Wrap(address indexed to, UFixed18 amount);
event Unwrap(address indexed to, UFixed18 amount);
event Rebalance(UFixed18 newMinted, UFixed18 newRedeemed);
event Close(UFixed18 amount);
error BatcherNotImplementedError();
error BatcherBalanceMismatchError(UFixed18 oldBalance, UFixed18 newBalance);
function RESERVE() external view returns (IEmptySetReserve); // solhint-disable-line func-name-mixedcase
function USDC() external view returns (Token6); // solhint-disable-line func-name-mixedcase
function DSU() external view returns (Token18); // solhint-disable-line func-name-mixedcase
function totalBalance() external view returns (UFixed18);
function wrap(UFixed18 amount, address to) external;
function unwrap(UFixed18 amount, address to) external;
function rebalance() external;
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
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 amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` 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 amount) 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 `amount` 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 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` 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 amount
) external returns (bool);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)
pragma solidity ^0.8.0;
import "../IERC20.sol";
/**
* @dev Interface for the optional metadata functions from the ERC20 standard.
*
* _Available since v4.1._
*/
interface IERC20Metadata is IERC20 {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
}
//SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.13;
import "@openzeppelin/contracts/interfaces/IERC20.sol";
import "./IBatcher.sol";
interface ITwoWayBatcher is IBatcher, IERC20 {
/// @notice Event emitted on USDC deposit
event Deposit(address indexed account, UFixed18 amount);
/// @notice Event emitted on USDC withdraw
event Withdraw(address indexed account, UFixed18 amount);
/// @notice Error thrown on invalid USDC amount
error TwoWayBatcherInvalidTokenAmount(UFixed18 amount);
/// @notice Deposits USDC for Batcher to use in unwrapping flows
function deposit(UFixed18 amount) external;
/// @notice Withdraws USDC from Batcher
function withdraw(UFixed18 amount) external;
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (utils/math/Math.sol)
pragma solidity ^0.8.0;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a >= b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return 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 up instead
* of rounding down.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b - 1) / b can overflow on addition, so we distribute.
return a / b + (a % b == 0 ? 0 : 1);
}
}
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.13;
import "./Fixed18.sol";
/// @dev PackedFixed18 type
type PackedFixed18 is int128;
using PackedFixed18Lib for PackedFixed18 global;
/**
* @title PackedFixed18Lib
* @dev A packed version of the Fixed18 which takes up half the storage space (two PackedFixed18 can be packed
* into a single slot). Only valid within the range -1.7014118e+20 <= x <= 1.7014118e+20.
* @notice Library for the packed signed fixed-decimal type.
*/
library PackedFixed18Lib {
PackedFixed18 public constant MAX = PackedFixed18.wrap(type(int128).max);
PackedFixed18 public constant MIN = PackedFixed18.wrap(type(int128).min);
/**
* @notice Creates a signed fixed-decimal from a sign and an unsigned fixed-decimal
* @param self Sign
* @return New signed fixed-decimal
*/
function unpack(PackedFixed18 self) internal pure returns (Fixed18) {
return Fixed18.wrap(int256(PackedFixed18.unwrap(self)));
}
}
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.13;
import "./UFixed18.sol";
/// @dev PackedUFixed18 type
type PackedUFixed18 is uint128;
using PackedUFixed18Lib for PackedUFixed18 global;
/**
* @title PackedUFixed18Lib
* @dev A packed version of the UFixed18 which takes up half the storage space (two PackedUFixed18 can be packed
* into a single slot). Only valid within the range 0 <= x <= 3.4028237e+20.
* @notice Library for the packed unsigned fixed-decimal type.
*/
library PackedUFixed18Lib {
PackedUFixed18 public constant MAX = PackedUFixed18.wrap(type(uint128).max);
/**
* @notice Creates a signed fixed-decimal from a sign and an unsigned fixed-decimal
* @param self Sign
* @return New signed fixed-decimal
*/
function unpack(PackedUFixed18 self) internal pure returns (UFixed18) {
return UFixed18.wrap(uint256(PackedUFixed18.unwrap(self)));
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.0;
import "../IERC20.sol";
import "../../../utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using Address for address;
function safeTransfer(
IERC20 token,
address to,
uint256 value
) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
function safeTransferFrom(
IERC20 token,
address from,
address to,
uint256 value
) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
/**
* @dev Deprecated. This function has issues similar to the ones found in
* {IERC20-approve}, and its usage is discouraged.
*
* Whenever possible, use {safeIncreaseAllowance} and
* {safeDecreaseAllowance} instead.
*/
function safeApprove(
IERC20 token,
address spender,
uint256 value
) internal {
// safeApprove should only be called when setting an initial allowance,
// or when resetting it to zero. To increase and decrease it, use
// 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
require(
(value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
function safeIncreaseAllowance(
IERC20 token,
address spender,
uint256 value
) internal {
uint256 newAllowance = token.allowance(address(this), spender) + value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
function safeDecreaseAllowance(
IERC20 token,
address spender,
uint256 value
) internal {
unchecked {
uint256 oldAllowance = token.allowance(address(this), spender);
require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
uint256 newAllowance = oldAllowance - value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
if (returndata.length > 0) {
// Return data is optional
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (utils/math/SignedMath.sol)
pragma solidity ^0.8.0;
/**
* @dev Standard signed math utilities missing in the Solidity language.
*/
library SignedMath {
/**
* @dev Returns the largest of two signed numbers.
*/
function max(int256 a, int256 b) internal pure returns (int256) {
return a >= b ? a : b;
}
/**
* @dev Returns the smallest of two signed numbers.
*/
function min(int256 a, int256 b) internal pure returns (int256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two signed numbers without overflow.
* The result is rounded towards zero.
*/
function average(int256 a, int256 b) internal pure returns (int256) {
// Formula from the book "Hacker's Delight"
int256 x = (a & b) + ((a ^ b) >> 1);
return x + (int256(uint256(x) >> 255) & (a ^ b));
}
/**
* @dev Returns the absolute unsigned value of a signed value.
*/
function abs(int256 n) internal pure returns (uint256) {
unchecked {
// must be unchecked in order to support `n = type(int256).min`
return uint256(n >= 0 ? n : -n);
}
}
}
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.13;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "../../number/types/UFixed18.sol";
/// @dev Token18
type Token18 is address;
using Token18Lib for Token18 global;
type Token18Storage is bytes32;
using Token18StorageLib for Token18Storage global;
/**
* @title Token18Lib
* @notice Library to manage 18-decimal ERC20s that is compliant with the fixed-decimal types.
* @dev Maintains significant gas savings over other Token implementations since no conversion take place
*/
library Token18Lib {
using SafeERC20 for IERC20;
Token18 public constant ZERO = Token18.wrap(address(0));
/**
* @notice Returns whether a token is the zero address
* @param self Token to check for
* @return Whether the token is the zero address
*/
function isZero(Token18 self) internal pure returns (bool) {
return Token18.unwrap(self) == Token18.unwrap(ZERO);
}
/**
* @notice Returns whether the two tokens are equal
* @param a First token to compare
* @param b Second token to compare
* @return Whether the two tokens are equal
*/
function eq(Token18 a, Token18 b) internal pure returns (bool) {
return Token18.unwrap(a) == Token18.unwrap(b);
}
/**
* @notice Approves `grantee` to spend infinite tokens from the caller
* @param self Token to transfer
* @param grantee Address to allow spending
*/
function approve(Token18 self, address grantee) internal {
IERC20(Token18.unwrap(self)).safeApprove(grantee, type(uint256).max);
}
/**
* @notice Approves `grantee` to spend `amount` tokens from the caller
* @param self Token to transfer
* @param grantee Address to allow spending
* @param amount Amount of tokens to approve to spend
*/
function approve(Token18 self, address grantee, UFixed18 amount) internal {
IERC20(Token18.unwrap(self)).safeApprove(grantee, UFixed18.unwrap(amount));
}
/**
* @notice Transfers all held tokens from the caller to the `recipient`
* @param self Token to transfer
* @param recipient Address to receive the tokens
*/
function push(Token18 self, address recipient) internal {
push(self, recipient, balanceOf(self, address(this)));
}
/**
* @notice Transfers `amount` tokens from the caller to the `recipient`
* @param self Token to transfer
* @param recipient Address to transfer tokens to
* @param amount Amount of tokens to transfer
*/
function push(Token18 self, address recipient, UFixed18 amount) internal {
IERC20(Token18.unwrap(self)).safeTransfer(recipient, UFixed18.unwrap(amount));
}
/**
* @notice Transfers `amount` tokens from the `benefactor` to the caller
* @dev Reverts if trying to pull Ether
* @param self Token to transfer
* @param benefactor Address to transfer tokens from
* @param amount Amount of tokens to transfer
*/
function pull(Token18 self, address benefactor, UFixed18 amount) internal {
IERC20(Token18.unwrap(self)).safeTransferFrom(benefactor, address(this), UFixed18.unwrap(amount));
}
/**
* @notice Transfers `amount` tokens from the `benefactor` to `recipient`
* @dev Reverts if trying to pull Ether
* @param self Token to transfer
* @param benefactor Address to transfer tokens from
* @param recipient Address to transfer tokens to
* @param amount Amount of tokens to transfer
*/
function pullTo(Token18 self, address benefactor, address recipient, UFixed18 amount) internal {
IERC20(Token18.unwrap(self)).safeTransferFrom(benefactor, recipient, UFixed18.unwrap(amount));
}
/**
* @notice Returns the name of the token
* @param self Token to check for
* @return Token name
*/
function name(Token18 self) internal view returns (string memory) {
return IERC20Metadata(Token18.unwrap(self)).name();
}
/**
* @notice Returns the symbol of the token
* @param self Token to check for
* @return Token symbol
*/
function symbol(Token18 self) internal view returns (string memory) {
return IERC20Metadata(Token18.unwrap(self)).symbol();
}
/**
* @notice Returns the `self` token balance of the caller
* @param self Token to check for
* @return Token balance of the caller
*/
function balanceOf(Token18 self) internal view returns (UFixed18) {
return balanceOf(self, address(this));
}
/**
* @notice Returns the `self` token balance of `account`
* @param self Token to check for
* @param account Account to check
* @return Token balance of the account
*/
function balanceOf(Token18 self, address account) internal view returns (UFixed18) {
return UFixed18.wrap(IERC20(Token18.unwrap(self)).balanceOf(account));
}
}
library Token18StorageLib {
function read(Token18Storage self) internal view returns (Token18 value) {
assembly {
value := sload(self)
}
}
function store(Token18Storage self, Token18 value) internal {
assembly {
sstore(self, value)
}
}
}
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.13;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/utils/math/Math.sol";
import "../../number/types/UFixed18.sol";
/// @dev Token6
type Token6 is address;
using Token6Lib for Token6 global;
type Token6Storage is bytes32;
using Token6StorageLib for Token6Storage global;
/**
* @title Token6Lib
* @notice Library to manage 6-decimal ERC20s that is compliant with the fixed-decimal types.
* @dev Automatically converts from Base-6 token amounts to Base-18 UFixed18 amounts, with optional rounding
*/
library Token6Lib {
using SafeERC20 for IERC20;
Token6 public constant ZERO = Token6.wrap(address(0));
uint256 private constant OFFSET = 1e12;
/**
* @notice Returns whether a token is the zero address
* @param self Token to check for
* @return Whether the token is the zero address
*/
function isZero(Token6 self) internal pure returns (bool) {
return Token6.unwrap(self) == Token6.unwrap(ZERO);
}
/**
* @notice Returns whether the two tokens are equal
* @param a First token to compare
* @param b Second token to compare
* @return Whether the two tokens are equal
*/
function eq(Token6 a, Token6 b) internal pure returns (bool) {
return Token6.unwrap(a) == Token6.unwrap(b);
}
/**
* @notice Approves `grantee` to spend infinite tokens from the caller
* @param self Token to transfer
* @param grantee Address to allow spending
*/
function approve(Token6 self, address grantee) internal {
IERC20(Token6.unwrap(self)).safeApprove(grantee, type(uint256).max);
}
/**
* @notice Approves `grantee` to spend `amount` tokens from the caller
* @param self Token to transfer
* @param grantee Address to allow spending
* @param amount Amount of tokens to approve to spend
*/
function approve(Token6 self, address grantee, UFixed18 amount) internal {
IERC20(Token6.unwrap(self)).safeApprove(grantee, toTokenAmount(amount, false));
}
/**
* @notice Approves `grantee` to spend `amount` tokens from the caller
* @param self Token to transfer
* @param grantee Address to allow spending
* @param amount Amount of tokens to approve to spend
* @param roundUp Whether to round decimal token amount up to the next unit
*/
function approve(Token6 self, address grantee, UFixed18 amount, bool roundUp) internal {
IERC20(Token6.unwrap(self)).safeApprove(grantee, toTokenAmount(amount, roundUp));
}
/**
* @notice Transfers all held tokens from the caller to the `recipient`
* @param self Token to transfer
* @param recipient Address to receive the tokens
*/
function push(Token6 self, address recipient) internal {
push(self, recipient, balanceOf(self, address(this)));
}
/**
* @notice Transfers `amount` tokens from the caller to the `recipient`
* @param self Token to transfer
* @param recipient Address to transfer tokens to
* @param amount Amount of tokens to transfer
*/
function push(Token6 self, address recipient, UFixed18 amount) internal {
IERC20(Token6.unwrap(self)).safeTransfer(recipient, toTokenAmount(amount, false));
}
/**
* @notice Transfers `amount` tokens from the caller to the `recipient`
* @param self Token to transfer
* @param recipient Address to transfer tokens to
* @param amount Amount of tokens to transfer
* @param roundUp Whether to round decimal token amount up to the next unit
*/
function push(Token6 self, address recipient, UFixed18 amount, bool roundUp) internal {
IERC20(Token6.unwrap(self)).safeTransfer(recipient, toTokenAmount(amount, roundUp));
}
/**
* @notice Transfers `amount` tokens from the `benefactor` to the caller
* @dev Reverts if trying to pull Ether
* @param self Token to transfer
* @param benefactor Address to transfer tokens from
* @param amount Amount of tokens to transfer
*/
function pull(Token6 self, address benefactor, UFixed18 amount) internal {
IERC20(Token6.unwrap(self)).safeTransferFrom(benefactor, address(this), toTokenAmount(amount, false));
}
/**
* @notice Transfers `amount` tokens from the `benefactor` to the caller
* @dev Reverts if trying to pull Ether
* @param self Token to transfer
* @param benefactor Address to transfer tokens from
* @param amount Amount of tokens to transfer
* @param roundUp Whether to round decimal token amount up to the next unit
*/
function pull(Token6 self, address benefactor, UFixed18 amount, bool roundUp) internal {
IERC20(Token6.unwrap(self)).safeTransferFrom(benefactor, address(this), toTokenAmount(amount, roundUp));
}
/**
* @notice Transfers `amount` tokens from the `benefactor` to `recipient`
* @dev Reverts if trying to pull Ether
* @param self Token to transfer
* @param benefactor Address to transfer tokens from
* @param recipient Address to transfer tokens to
* @param amount Amount of tokens to transfer
*/
function pullTo(Token6 self, address benefactor, address recipient, UFixed18 amount) internal {
IERC20(Token6.unwrap(self)).safeTransferFrom(benefactor, recipient, toTokenAmount(amount, false));
}
/**
* @notice Transfers `amount` tokens from the `benefactor` to `recipient`
* @dev Reverts if trying to pull Ether
* @param self Token to transfer
* @param benefactor Address to transfer tokens from
* @param recipient Address to transfer tokens to
* @param amount Amount of tokens to transfer
* @param roundUp Whether to round decimal token amount up to the next unit
*/
function pullTo(Token6 self, address benefactor, address recipient, UFixed18 amount, bool roundUp) internal {
IERC20(Token6.unwrap(self)).safeTransferFrom(benefactor, recipient, toTokenAmount(amount, roundUp));
}
/**
* @notice Returns the name of the token
* @param self Token to check for
* @return Token name
*/
function name(Token6 self) internal view returns (string memory) {
return IERC20Metadata(Token6.unwrap(self)).name();
}
/**
* @notice Returns the symbol of the token
* @param self Token to check for
* @return Token symbol
*/
function symbol(Token6 self) internal view returns (string memory) {
return IERC20Metadata(Token6.unwrap(self)).symbol();
}
/**
* @notice Returns the `self` token balance of the caller
* @param self Token to check for
* @return Token balance of the caller
*/
function balanceOf(Token6 self) internal view returns (UFixed18) {
return balanceOf(self, address(this));
}
/**
* @notice Returns the `self` token balance of `account`
* @param self Token to check for
* @param account Account to check
* @return Token balance of the account
*/
function balanceOf(Token6 self, address account) internal view returns (UFixed18) {
return fromTokenAmount(IERC20(Token6.unwrap(self)).balanceOf(account));
}
/**
* @notice Converts the unsigned fixed-decimal amount into the token amount according to
* it's defined decimals
* @dev Provides the ability to "round up" the token amount which is useful in situations where
* are swapping one token for another and don't want to give away "free" units due to rounding
* errors in the favor of the user.
* @param amount Amount to convert
* @param roundUp Whether to round decimal token amount up to the next unit
* @return Normalized token amount
*/
function toTokenAmount(UFixed18 amount, bool roundUp) private pure returns (uint256) {
return roundUp ? Math.ceilDiv(UFixed18.unwrap(amount), OFFSET) : UFixed18.unwrap(amount) / OFFSET;
}
/**
* @notice Converts the token amount into the unsigned fixed-decimal amount according to
* it's defined decimals
* @param amount Token amount to convert
* @return Normalized unsigned fixed-decimal amount
*/
function fromTokenAmount(uint256 amount) private pure returns (UFixed18) {
return UFixed18.wrap(amount * OFFSET);
}
}
library Token6StorageLib {
function read(Token6Storage self) internal view returns (Token6 value) {
assembly {
value := sload(self)
}
}
function store(Token6Storage self, Token6 value) internal {
assembly {
sstore(self, value)
}
}
}
//SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.17;
import "@equilibria/root/number/types/UFixed18.sol";
import "@equilibria/root/token/types/Token18.sol";
import "@equilibria/root/token/types/Token6.sol";
import "@equilibria/root/control/unstructured/UReentrancyGuard.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "../interfaces/ITwoWayBatcher.sol";
import "./Batcher.sol";
contract TwoWayBatcher is ITwoWayBatcher, UReentrancyGuard, Batcher, ERC20 {
/**
* @notice Initializes the TwoWayBatcher
* @dev Called at implementation instantiate and constant for that implementation.
* @param reserve EmptySet Reserve Aaddress
* @param dsu DSU Token address
* @param usdc USDC Token Address
*/
constructor(IEmptySetReserve reserve, Token18 dsu, Token6 usdc)
Batcher(reserve, dsu, usdc)
ERC20("Batcher Deposit", "BDEP")
{
__UReentrancyGuard__initialize();
}
/**
* @notice Deposits USDC for Batcher to use in unwrapping flows
* @dev Reverts if `amount` has greater precision than 6 decimals
* @param amount Amount of USDC to deposit
*/
function deposit(UFixed18 amount) external nonReentrant {
if (!_validToken6Amount(amount)) revert TwoWayBatcherInvalidTokenAmount(amount);
rebalance();
USDC.pull(msg.sender, amount, true);
_mint(msg.sender, UFixed18.unwrap(amount));
emit Deposit(msg.sender, amount);
}
/**
* @notice Withdraws USDC from Batcher
* @dev Reverts if `amount` has greater precision than 6 decimals
* @param amount Amount of USDC to withdraw
*/
function withdraw(UFixed18 amount) external nonReentrant {
if (!_validToken6Amount(amount)) revert TwoWayBatcherInvalidTokenAmount(amount);
rebalance();
_burn(msg.sender, UFixed18.unwrap(amount));
USDC.push(msg.sender, amount);
emit Withdraw(msg.sender, amount);
}
/**
* @notice Rebalances the Batcher to maintain a target balance of USDC and DSU
* @dev Maintains a USDC balance of outstanding deposits. Excess USDC is minted as DSU
* @param usdcBalance Current Batcher USDC balance
*/
function _rebalance(UFixed18 usdcBalance, UFixed18) override internal {
UFixed18 totalDeposits = UFixed18.wrap(totalSupply());
uint256 balanceToTarget = usdcBalance.compare(totalDeposits);
// totalDeposits == usdcBalance: Do nothing
if (balanceToTarget == 1) return;
// usdcBalance > totalDeposits: deposit excess USDC
if (balanceToTarget == 2) return RESERVE.mint(usdcBalance.sub(totalDeposits));
// usdcBalance < totalDeposits: pull out more USDC so we have enough to cover deposits
if (balanceToTarget == 0) return RESERVE.redeem(totalDeposits.sub(usdcBalance));
}
/**
* @notice Performs actions required to close the Batcher
*/
function _close() override internal {
rebalance();
}
/**
* @notice Checks if the `amount` has a maximum precision of 6 decimals
* @return true if the `amount` has a precision of 6 or less, otherwise false
*/
function _validToken6Amount(UFixed18 amount) internal pure returns (bool) {
return UFixed18.unwrap(amount) % 1e12 == 0;
}
}
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.13;
import "@openzeppelin/contracts/utils/math/Math.sol";
import "./Fixed18.sol";
import "./PackedUFixed18.sol";
/// @dev UFixed18 type
type UFixed18 is uint256;
using UFixed18Lib for UFixed18 global;
type UFixed18Storage is bytes32;
using UFixed18StorageLib for UFixed18Storage global;
/**
* @title UFixed18Lib
* @notice Library for the unsigned fixed-decimal type.
*/
library UFixed18Lib {
error UFixed18UnderflowError(int256 value);
error UFixed18PackingOverflowError(uint256 value);
uint256 private constant BASE = 1e18;
UFixed18 public constant ZERO = UFixed18.wrap(0);
UFixed18 public constant ONE = UFixed18.wrap(BASE);
UFixed18 public constant MAX = UFixed18.wrap(type(uint256).max);
/**
* @notice Creates a unsigned fixed-decimal from a signed fixed-decimal
* @param a Signed fixed-decimal
* @return New unsigned fixed-decimal
*/
function from(Fixed18 a) internal pure returns (UFixed18) {
int256 value = Fixed18.unwrap(a);
if (value < 0) revert UFixed18UnderflowError(value);
return UFixed18.wrap(uint256(value));
}
/**
* @notice Creates a unsigned fixed-decimal from a unsigned integer
* @param a Unsigned number
* @return New unsigned fixed-decimal
*/
function from(uint256 a) internal pure returns (UFixed18) {
return UFixed18.wrap(a * BASE);
}
/**
* @notice Creates a packed unsigned fixed-decimal from an unsigned fixed-decimal
* @param a unsigned fixed-decimal
* @return New packed unsigned fixed-decimal
*/
function pack(UFixed18 a) internal pure returns (PackedUFixed18) {
uint256 value = UFixed18.unwrap(a);
if (value > type(uint128).max) revert UFixed18PackingOverflowError(value);
return PackedUFixed18.wrap(uint128(value));
}
/**
* @notice Returns whether the unsigned fixed-decimal is equal to zero.
* @param a Unsigned fixed-decimal
* @return Whether the unsigned fixed-decimal is zero.
*/
function isZero(UFixed18 a) internal pure returns (bool) {
return UFixed18.unwrap(a) == 0;
}
/**
* @notice Adds two unsigned fixed-decimals `a` and `b` together
* @param a First unsigned fixed-decimal
* @param b Second unsigned fixed-decimal
* @return Resulting summed unsigned fixed-decimal
*/
function add(UFixed18 a, UFixed18 b) internal pure returns (UFixed18) {
return UFixed18.wrap(UFixed18.unwrap(a) + UFixed18.unwrap(b));
}
/**
* @notice Subtracts unsigned fixed-decimal `b` from `a`
* @param a Unsigned fixed-decimal to subtract from
* @param b Unsigned fixed-decimal to subtract
* @return Resulting subtracted unsigned fixed-decimal
*/
function sub(UFixed18 a, UFixed18 b) internal pure returns (UFixed18) {
return UFixed18.wrap(UFixed18.unwrap(a) - UFixed18.unwrap(b));
}
/**
* @notice Multiplies two unsigned fixed-decimals `a` and `b` together
* @param a First unsigned fixed-decimal
* @param b Second unsigned fixed-decimal
* @return Resulting multiplied unsigned fixed-decimal
*/
function mul(UFixed18 a, UFixed18 b) internal pure returns (UFixed18) {
return UFixed18.wrap(UFixed18.unwrap(a) * UFixed18.unwrap(b) / BASE);
}
/**
* @notice Divides unsigned fixed-decimal `a` by `b`
* @param a Unsigned fixed-decimal to divide
* @param b Unsigned fixed-decimal to divide by
* @return Resulting divided unsigned fixed-decimal
*/
function div(UFixed18 a, UFixed18 b) internal pure returns (UFixed18) {
return UFixed18.wrap(UFixed18.unwrap(a) * BASE / UFixed18.unwrap(b));
}
/**
* @notice Divides unsigned fixed-decimal `a` by `b`
* @dev Does not revert on divide-by-0, instead returns `ONE` for `0/0` and `MAX` for `n/0`.
* @param a Unsigned fixed-decimal to divide
* @param b Unsigned fixed-decimal to divide by
* @return Resulting divided unsigned fixed-decimal
*/
function unsafeDiv(UFixed18 a, UFixed18 b) internal pure returns (UFixed18) {
if (isZero(b)) {
return isZero(a) ? ONE : MAX;
} else {
return div(a, b);
}
}
/**
* @notice Computes a * b / c without loss of precision due to BASE conversion
* @param a First unsigned fixed-decimal
* @param b Unsigned number to multiply by
* @param c Unsigned number to divide by
* @return Resulting computation
*/
function muldiv(UFixed18 a, uint256 b, uint256 c) internal pure returns (UFixed18) {
return muldiv(a, UFixed18.wrap(b), UFixed18.wrap(c));
}
/**
* @notice Computes a * b / c without loss of precision due to BASE conversion
* @param a First unsigned fixed-decimal
* @param b Unsigned fixed-decimal to multiply by
* @param c Unsigned fixed-decimal to divide by
* @return Resulting computation
*/
function muldiv(UFixed18 a, UFixed18 b, UFixed18 c) internal pure returns (UFixed18) {
return UFixed18.wrap(UFixed18.unwrap(a) * UFixed18.unwrap(b) / UFixed18.unwrap(c));
}
/**
* @notice Returns whether unsigned fixed-decimal `a` is equal to `b`
* @param a First unsigned fixed-decimal
* @param b Second unsigned fixed-decimal
* @return Whether `a` is equal to `b`
*/
function eq(UFixed18 a, UFixed18 b) internal pure returns (bool) {
return compare(a, b) == 1;
}
/**
* @notice Returns whether unsigned fixed-decimal `a` is greater than `b`
* @param a First unsigned fixed-decimal
* @param b Second unsigned fixed-decimal
* @return Whether `a` is greater than `b`
*/
function gt(UFixed18 a, UFixed18 b) internal pure returns (bool) {
return compare(a, b) == 2;
}
/**
* @notice Returns whether unsigned fixed-decimal `a` is less than `b`
* @param a First unsigned fixed-decimal
* @param b Second unsigned fixed-decimal
* @return Whether `a` is less than `b`
*/
function lt(UFixed18 a, UFixed18 b) internal pure returns (bool) {
return compare(a, b) == 0;
}
/**
* @notice Returns whether unsigned fixed-decimal `a` is greater than or equal to `b`
* @param a First unsigned fixed-decimal
* @param b Second unsigned fixed-decimal
* @return Whether `a` is greater than or equal to `b`
*/
function gte(UFixed18 a, UFixed18 b) internal pure returns (bool) {
return gt(a, b) || eq(a, b);
}
/**
* @notice Returns whether unsigned fixed-decimal `a` is less than or equal to `b`
* @param a First unsigned fixed-decimal
* @param b Second unsigned fixed-decimal
* @return Whether `a` is less than or equal to `b`
*/
function lte(UFixed18 a, UFixed18 b) internal pure returns (bool) {
return lt(a, b) || eq(a, b);
}
/**
* @notice Compares the unsigned fixed-decimals `a` and `b`
* @dev Returns: 2 for greater than
* 1 for equal to
* 0 for less than
* @param a First unsigned fixed-decimal
* @param b Second unsigned fixed-decimal
* @return Compare result of `a` and `b`
*/
function compare(UFixed18 a, UFixed18 b) internal pure returns (uint256) {
(uint256 au, uint256 bu) = (UFixed18.unwrap(a), UFixed18.unwrap(b));
if (au > bu) return 2;
if (au < bu) return 0;
return 1;
}
/**
* @notice Returns a unsigned fixed-decimal representing the ratio of `a` over `b`
* @param a First unsigned number
* @param b Second unsigned number
* @return Ratio of `a` over `b`
*/
function ratio(uint256 a, uint256 b) internal pure returns (UFixed18) {
return UFixed18.wrap(a * BASE / b);
}
/**
* @notice Returns the minimum of unsigned fixed-decimals `a` and `b`
* @param a First unsigned fixed-decimal
* @param b Second unsigned fixed-decimal
* @return Minimum of `a` and `b`
*/
function min(UFixed18 a, UFixed18 b) internal pure returns (UFixed18) {
return UFixed18.wrap(Math.min(UFixed18.unwrap(a), UFixed18.unwrap(b)));
}
/**
* @notice Returns the maximum of unsigned fixed-decimals `a` and `b`
* @param a First unsigned fixed-decimal
* @param b Second unsigned fixed-decimal
* @return Maximum of `a` and `b`
*/
function max(UFixed18 a, UFixed18 b) internal pure returns (UFixed18) {
return UFixed18.wrap(Math.max(UFixed18.unwrap(a), UFixed18.unwrap(b)));
}
/**
* @notice Converts the unsigned fixed-decimal into an integer, truncating any decimal portion
* @param a Unsigned fixed-decimal
* @return Truncated unsigned number
*/
function truncate(UFixed18 a) internal pure returns (uint256) {
return UFixed18.unwrap(a) / BASE;
}
}
library UFixed18StorageLib {
function read(UFixed18Storage self) internal view returns (UFixed18 value) {
assembly {
value := sload(self)
}
}
function store(UFixed18Storage self, UFixed18 value) internal {
assembly {
sstore(self, value)
}
}
}
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.13;
import "@openzeppelin/contracts/utils/Address.sol";
import "../../storage/UStorage.sol";
/**
* @title UInitializable
* @notice Library to manage the initialization lifecycle of upgradeable contracts
* @dev `UInitializable` allows the creation of pseudo-constructors for upgradeable contracts. One
* `initializer` should be declared per top-level contract. Child contracts can use the `onlyInitializer`
* modifier to tag their internal initialization functions to ensure that they can only be called
* from a top-level `initializer` or a constructor.
*/
abstract contract UInitializable {
error UInitializableZeroVersionError();
error UInitializableAlreadyInitializedError(uint256 version);
error UInitializableNotInitializingError();
/// @dev The initialized flag
Uint256Storage private constant _version = Uint256Storage.wrap(keccak256("equilibria.root.UInitializable.version"));
/// @dev The initializing flag
BoolStorage private constant _initializing = BoolStorage.wrap(keccak256("equilibria.root.UInitializable.initializing"));
/// @dev Can only be called once per version, `version` is 1-indexed
modifier initializer(uint256 version) {
if (version == 0) revert UInitializableZeroVersionError();
if (_version.read() >= version) revert UInitializableAlreadyInitializedError(version);
_version.store(version);
_initializing.store(true);
_;
_initializing.store(false);
}
/// @dev Can only be called from an initializer or constructor
modifier onlyInitializer() {
if (!_constructing() && !_initializing.read()) revert UInitializableNotInitializingError();
_;
}
/**
* @notice Returns whether the contract is currently being constructed
* @dev {Address.isContract} returns false for contracts currently in the process of being constructed
* @return Whether the contract is currently being constructed
*/
function _constructing() private view returns (bool) {
return !Address.isContract(address(this));
}
}
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.13;
import "./UInitializable.sol";
import "../../storage/UStorage.sol";
/**
* @title UOwnable
* @notice Library to manage the ownership lifecycle of upgradeable contracts.
* @dev This contract has been extended from the Open Zeppelin library to include an
* unstructured storage pattern so that it can be safely mixed in with upgradeable
* contracts without affecting their storage patterns through inheritance.
*/
abstract contract UOwnable is UInitializable {
event OwnerUpdated(address indexed newOwner);
event PendingOwnerUpdated(address indexed newPendingOwner);
error UOwnableNotOwnerError(address sender);
error UOwnableNotPendingOwnerError(address sender);
/// @dev The owner address
AddressStorage private constant _owner = AddressStorage.wrap(keccak256("equilibria.root.UOwnable.owner"));
function owner() public view returns (address) { return _owner.read(); }
/// @dev The pending owner address
AddressStorage private constant _pendingOwner = AddressStorage.wrap(keccak256("equilibria.root.UOwnable.pendingOwner"));
function pendingOwner() public view returns (address) { return _pendingOwner.read(); }
/**
* @notice Initializes the contract setting `msg.sender` as the initial owner
*/
function __UOwnable__initialize() internal onlyInitializer {
_updateOwner(msg.sender);
}
/**
* @notice Updates the new pending owner
* @dev Can only be called by the current owner
* New owner does not take affect until that address calls `acceptOwner()`
* @param newPendingOwner New pending owner address
*/
function updatePendingOwner(address newPendingOwner) public onlyOwner {
_pendingOwner.store(newPendingOwner);
emit PendingOwnerUpdated(newPendingOwner);
}
/**
* @notice Accepts and transfers the ownership of the contract to the pending owner
* @dev Can only be called by the pending owner to ensure correctness
*/
function acceptOwner() external {
if (msg.sender != pendingOwner()) revert UOwnableNotPendingOwnerError(msg.sender);
_updateOwner(pendingOwner());
updatePendingOwner(address(0));
}
/**
* @notice Updates the owner address
* @param newOwner New owner address
*/
function _updateOwner(address newOwner) private {
_owner.store(newOwner);
emit OwnerUpdated(newOwner);
}
/// @dev Throws if called by any account other than the owner
modifier onlyOwner() {
if (owner() != msg.sender) revert UOwnableNotOwnerError(msg.sender);
_;
}
}
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.13;
import "./UInitializable.sol";
import "../../storage/UStorage.sol";
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*
* NOTE: This contract has been extended from the Open Zeppelin library to include an
* unstructured storage pattern, so that it can be safely mixed in with upgradeable
* contracts without affecting their storage patterns through inheritance.
*/
abstract contract UReentrancyGuard is UInitializable {
error UReentrancyGuardReentrantCallError();
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
/**
* @dev unstructured storage slot for the reentrancy status
*/
Uint256Storage private constant _status = Uint256Storage.wrap(keccak256("equilibria.root.UReentrancyGuard.status"));
/**
* @dev Initializes the contract setting the status to _NOT_ENTERED.
*/
function __UReentrancyGuard__initialize() internal onlyInitializer {
_status.store(_NOT_ENTERED);
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and make it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
// On the first call to nonReentrant, _notEntered will be true
if (_status.read() == _ENTERED) revert UReentrancyGuardReentrantCallError();
// Any calls to nonReentrant after this point will fail
_status.store(_ENTERED);
_;
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status.store(_NOT_ENTERED);
}
}
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.13;
import "../number/types/UFixed18.sol";
type BoolStorage is bytes32;
using BoolStorageLib for BoolStorage global;
type Uint256Storage is bytes32;
using Uint256StorageLib for Uint256Storage global;
type Int256Storage is bytes32;
using Int256StorageLib for Int256Storage global;
type AddressStorage is bytes32;
using AddressStorageLib for AddressStorage global;
type Bytes32Storage is bytes32;
using Bytes32StorageLib for Bytes32Storage global;
library BoolStorageLib {
function read(BoolStorage self) internal view returns (bool value) {
assembly {
value := sload(self)
}
}
function store(BoolStorage self, bool value) internal {
assembly {
sstore(self, value)
}
}
}
library Uint256StorageLib {
function read(Uint256Storage self) internal view returns (uint256 value) {
assembly {
value := sload(self)
}
}
function store(Uint256Storage self, uint256 value) internal {
assembly {
sstore(self, value)
}
}
}
library Int256StorageLib {
function read(Int256Storage self) internal view returns (int256 value) {
assembly {
value := sload(self)
}
}
function store(Int256Storage self, int256 value) internal {
assembly {
sstore(self, value)
}
}
}
library AddressStorageLib {
function read(AddressStorage self) internal view returns (address value) {
assembly {
value := sload(self)
}
}
function store(AddressStorage self, address value) internal {
assembly {
sstore(self, value)
}
}
}
library Bytes32StorageLib {
function read(Bytes32Storage self) internal view returns (bytes32 value) {
assembly {
value := sload(self)
}
}
function store(Bytes32Storage self, bytes32 value) internal {
assembly {
sstore(self, value)
}
}
}
{
"compilationTarget": {
"contracts/batcher/TwoWayBatcher.sol": "TwoWayBatcher"
},
"evmVersion": "london",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs",
"useLiteralContent": true
},
"optimizer": {
"enabled": true,
"runs": 1000000
},
"remappings": []
}
[{"inputs":[{"internalType":"contract IEmptySetReserve","name":"reserve","type":"address"},{"internalType":"Token18","name":"dsu","type":"address"},{"internalType":"Token6","name":"usdc","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"UFixed18","name":"oldBalance","type":"uint256"},{"internalType":"UFixed18","name":"newBalance","type":"uint256"}],"name":"BatcherBalanceMismatchError","type":"error"},{"inputs":[],"name":"BatcherNotImplementedError","type":"error"},{"inputs":[{"internalType":"UFixed18","name":"amount","type":"uint256"}],"name":"TwoWayBatcherInvalidTokenAmount","type":"error"},{"inputs":[{"internalType":"uint256","name":"version","type":"uint256"}],"name":"UInitializableAlreadyInitializedError","type":"error"},{"inputs":[],"name":"UInitializableNotInitializingError","type":"error"},{"inputs":[],"name":"UInitializableZeroVersionError","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"}],"name":"UOwnableNotOwnerError","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"}],"name":"UOwnableNotPendingOwnerError","type":"error"},{"inputs":[],"name":"UReentrancyGuardReentrantCallError","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"UFixed18","name":"amount","type":"uint256"}],"name":"Close","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"UFixed18","name":"amount","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnerUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"newPendingOwner","type":"address"}],"name":"PendingOwnerUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"UFixed18","name":"newMinted","type":"uint256"},{"indexed":false,"internalType":"UFixed18","name":"newRedeemed","type":"uint256"}],"name":"Rebalance","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"UFixed18","name":"amount","type":"uint256"}],"name":"Unwrap","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"UFixed18","name":"amount","type":"uint256"}],"name":"Withdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"UFixed18","name":"amount","type":"uint256"}],"name":"Wrap","type":"event"},{"inputs":[],"name":"DSU","outputs":[{"internalType":"Token18","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"RESERVE","outputs":[{"internalType":"contract IEmptySetReserve","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"USDC","outputs":[{"internalType":"Token6","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"acceptOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"close","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"UFixed18","name":"amount","type":"uint256"}],"name":"deposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rebalance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalBalance","outputs":[{"internalType":"UFixed18","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"UFixed18","name":"amount","type":"uint256"},{"internalType":"address","name":"to","type":"address"}],"name":"unwrap","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newPendingOwner","type":"address"}],"name":"updatePendingOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"UFixed18","name":"amount","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"UFixed18","name":"amount","type":"uint256"},{"internalType":"address","name":"to","type":"address"}],"name":"wrap","outputs":[],"stateMutability":"nonpayable","type":"function"}]