Contract Source Code:
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.23;
import {IPriceOracle} from "./price-oracles/interfaces/IPriceOracle.sol";
import {IOrderBook} from "./interfaces/IOrderBook.sol";
import {IPhutureOnDonationCallback} from "./interfaces/IPhutureOnDonationCallback.sol";
import {IVault} from "./interfaces/IVault.sol";
import {Currency, CurrencyLib} from "./libraries/CurrencyLib.sol";
import {FixedPointMathLib} from "solmate/utils/FixedPointMathLib.sol";
import {OrderLib} from "./libraries/OrderLib.sol";
import {PriceLib} from "./price-oracles/libraries/PriceLib.sol";
import {SafeCastLib} from "solmate/utils/SafeCastLib.sol";
import {Owned} from "solmate/auth/Owned.sol";
/// @title OrderBook
/// @notice This contract is responsible for setting up the execution of orders for the Index
contract OrderBook is IOrderBook, IPhutureOnDonationCallback, Owned {
using CurrencyLib for Currency;
using FixedPointMathLib for *;
using OrderLib for *;
using PriceLib for *;
using SafeCastLib for uint256;
uint16 internal constant MAX_BPS = 10_000;
/// @dev The address of the Vault contract
address public immutable vault;
/// @dev Addresses of fund managers allowed to execute trades
mapping(address => bool) public isFundManager;
/// @dev orderId => Set of orders
OrderLib.OrderRegistry internal _orders;
/// @dev The address of the Vault contract
mapping(bytes32 => uint256) internal _balancesOf;
/// @dev Number of orders to be received from the remote chains
int256 internal incomingOrders;
/// @notice The address of the Messenger contract
address public messenger;
/// @dev notice The address of the price oracle contract
address public priceOracle;
/// @dev The maximum slippage allowed in basis points
uint16 public maxSlippageInBP;
/// @notice Thrown when an unauthorized caller attempts to access a function
error Forbidden();
/// @notice Thrown when the amount exceeds the order size
error AmountExceedsOrderSize();
/// @notice Thrown when the donation fails
error DonateFailure();
/// @notice Thrown when the consume amount does not match the sold amount
error ConsumeAmountMismatch();
/// @notice Thrown when the orders are not sorted
error Sorting();
/// @notice Thrown when the sender is not authorized
/// @dev Only fund managers are allowed to execute trades
error BadSender();
/// @notice Thrown when the slippage is too high
error HighLoss();
/// @notice Thrown when the order is not empty
error NotEmpty();
/// @notice Thrown when the index is used more than once
error UsedIndex();
/// @notice Thrown when the length of arrays do not match
error LengthMismatch();
/// @notice Thrown when the number of orders does not match the expected count
error OrdersLength();
/// @dev Modifier to restrict access to a specific address
/// @param addr The address allowed to access the function
modifier only(address addr) {
if (msg.sender != addr) revert Forbidden();
_;
}
constructor(address _owner, address _vault, address _messenger, address _priceOracle, uint16 _maxSlippageInBP)
Owned(_owner)
{
messenger = _messenger;
vault = _vault;
priceOracle = _priceOracle;
maxSlippageInBP = _maxSlippageInBP;
}
receive() external payable {}
/// @notice Updates the fund manager status for the given address
///
/// @param fundManager The address of the fund manager
/// @param isAllowed The status of the fund manager (true for allowed, false for disallowed)
function updateFundManager(address fundManager, bool isAllowed) external onlyOwner {
isFundManager[fundManager] = isAllowed;
}
/// @notice Sets the address of the messenger contract
///
/// @param _messenger The address of the messenger contract
function setMessenger(address _messenger) external onlyOwner {
messenger = _messenger;
}
/// @notice Sets the address of the price oracle contract
///
/// @param _priceOracle The address of the price oracle contract
function setPriceOracle(address _priceOracle) external onlyOwner {
priceOracle = _priceOracle;
}
/// @notice Sets the maximum slippage allowed in basis points
///
/// @param _maxSlippageInBP The maximum slippage allowed in basis points
function setMaxSlippageInBP(uint16 _maxSlippageInBP) external onlyOwner {
maxSlippageInBP = _maxSlippageInBP;
}
/// @notice Sets the orders and the number of incoming orders
///
/// @param _incomingOrders The number of incoming orders
/// @param orders The orders to be set
function setOrders(uint256 _incomingOrders, OrderLib.Order[] calldata orders) external only(messenger) {
_orders.set(orders);
incomingOrders += int256(_incomingOrders);
}
/// @notice Receives the incoming orders from the remote chains
///
/// @param orders The incoming orders
/// @param currency The common sell currency for the orders
/// @param amount The total amount of the sell currency received
function receiveIncomingOrders(OrderLib.Order[] calldata orders, Currency currency, uint256 amount)
external
only(messenger)
{
_orders.set(orders);
incomingOrders -= int256(orders.length);
IVault(vault).donate(currency, abi.encodeCall(this.donate, (currency, amount)));
}
/// @notice Removes the dust orders
///
/// @param _incomingOrders The number of incoming orders to be removed
function removeDustOrders(uint256 _incomingOrders) external only(messenger) {
incomingOrders -= int256(_incomingOrders);
}
/// @notice Executes an order
/// @param params The parameters for executing the order
function executeOrder(ExecuteOrderParams calldata params) external {
if (!isFundManager[msg.sender]) revert BadSender();
if (_orders.get(params.orderId) < params.sell) revert AmountExceedsOrderSize();
bool isOutgoing = params.orderId.finalDestinationChainId != block.chainid;
address recipient = isOutgoing ? messenger : vault;
uint256 sellBalanceBefore = params.orderId.sellCurrency.balanceOf(vault);
uint256 buyBalanceBefore = params.orderId.localBuyCurrency.balanceOf(recipient);
if (isOutgoing) {
IVault(vault).consume(
params.orderId.sellCurrency, params.sell, params.tradeParams.target, params.tradeParams.data
);
} else {
IVault(vault).donate(
params.orderId.localBuyCurrency,
abi.encodeCall(this.consume, (params.orderId.sellCurrency, params.sell, params.tradeParams))
);
}
uint256 sold = sellBalanceBefore - params.orderId.sellCurrency.balanceOf(vault);
if (sold != params.sell) revert ConsumeAmountMismatch();
uint256 bought = params.orderId.localBuyCurrency.balanceOf(recipient) - buyBalanceBefore;
// stores `bought` amounts to create `pendingOrders` in `finishOrderExecution`
if (isOutgoing) _balancesOf[params.orderId.id()] += bought;
(uint256 sellCurrencyPrice, uint256 buyCurrencyPrice) = IPriceOracle(priceOracle).prices(
params.orderId.sellCurrency, params.orderId.localBuyCurrency, params.payload
);
if (
sold.convertToBaseDown(sellCurrencyPrice).mulDivDown(MAX_BPS - maxSlippageInBP, MAX_BPS)
> bought.convertToBaseDown(buyCurrencyPrice)
) revert HighLoss();
_orders.fill(params.orderId, sold.safeCastTo96());
emit OrderFilled(params.orderId.id(), params.sell, bought);
}
/// @notice Finishes the order execution and returns the pending orders
///
/// @dev `orderId` should be sorted by `finalDestinationChainId`
///
/// @param params The parameters for finishing the order execution
///
/// @return pendingOrders The pending orders to be processed
function finishOrderExecution(FinishOrderExecutionParams calldata params)
external
only(messenger)
returns (PendingOrder[] memory pendingOrders)
{
if (incomingOrders != 0) revert NotEmpty();
if (params.idIndices.length != params.orderIds.length) revert LengthMismatch();
_orders.reset(params.orderIds);
pendingOrders = new PendingOrder[](params.pendingOrderCounts.length);
uint256 orderIndex;
uint256 chainIndex;
uint256 lastRemoteChainId = type(uint256).max;
bool[] memory used = new bool[](params.orderIds.length);
for (uint256 i; i < params.orderIds.length; ++i) {
if (used[params.idIndices[i]]) revert UsedIndex();
used[params.idIndices[i]] = true;
OrderLib.OrderId calldata orderId = params.orderIds[params.idIndices[i]];
if (orderId.finalDestinationChainId != block.chainid) {
if (orderId.finalDestinationChainId != lastRemoteChainId) {
if (lastRemoteChainId != type(uint256).max) {
if (orderId.finalDestinationChainId < lastRemoteChainId) revert Sorting();
if (pendingOrders[chainIndex].orders.length != orderIndex) revert OrdersLength();
orderIndex = 0;
unchecked {
++chainIndex;
}
}
lastRemoteChainId = orderId.finalDestinationChainId;
pendingOrders[chainIndex].orders = new BoughtOrder[](params.pendingOrderCounts[chainIndex]);
pendingOrders[chainIndex].chainId = orderId.finalDestinationChainId;
pendingOrders[chainIndex].currency = orderId.localBuyCurrency;
}
bytes32 idKey = orderId.id();
uint256 bought = _balancesOf[idKey];
pendingOrders[chainIndex].totalBought += bought;
pendingOrders[chainIndex].orders[orderIndex] = BoughtOrder(bought, orderId.finalDestinationBuyCurrency);
delete _balancesOf[idKey];
unchecked {
++orderIndex;
}
}
}
if (pendingOrders.length != 0) {
if (params.pendingOrderCounts.length > chainIndex + 1) revert LengthMismatch();
if (pendingOrders[chainIndex].orders.length != orderIndex) revert OrdersLength();
}
}
/// @dev Callback function for handling donations to Vault
///
/// @param data The data passed from the Phuture callback
function phutureOnDonationCallbackV1(bytes memory data) external override only(vault) {
(bool success, bytes memory returnData) = address(this).call(data);
if (success) return;
if (returnData.length == 0) revert DonateFailure();
// if the call failed, bubble up the reason
/// @solidity memory-safe-assembly
assembly {
revert(add(returnData, 32), mload(returnData))
}
}
/// @notice Donates the specified amount of currency to the Vault
///
/// @param currency The currency to be donated
/// @param amount The amount of currency to be donated
function donate(Currency currency, uint256 amount) external only(address(this)) {
currency.transfer(vault, amount);
}
/// @notice Consumes the specified amount of currency from the Vault
///
/// @param currency The currency to be consumed
/// @param amount The amount of currency to be consumed
/// @param params The trade parameters for consuming the currency
function consume(Currency currency, uint96 amount, TradeParams memory params) external only(address(this)) {
IVault(vault).consume(currency, amount, params.target, params.data);
}
/// @notice Returns the order of the given orderId
///
/// @param orderId The orderId to be checked
///
/// @return The order amount
function orderOf(OrderLib.OrderId calldata orderId) external view returns (uint96) {
return _orders.get(orderId);
}
}
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.23;
import {Currency} from "../../libraries/CurrencyLib.sol";
/// @title PriceOracle interface
/// @notice Returns prices of Index's constituents
interface IPriceOracle {
function prices(Currency currency0, Currency currency1, bytes calldata) external view returns (uint256, uint256);
}
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.23;
import {Currency} from "../libraries/CurrencyLib.sol";
import {OrderLib} from "../libraries/OrderLib.sol";
/// @title IOrderBook interface
interface IOrderBook {
struct TradeParams {
address target;
bytes data;
}
struct BoughtOrder {
// Bought amount of local buy currency, sell amount on the remote
uint256 amount;
// Buy currency on the remote
Currency buyCurrency;
}
struct PendingOrder {
uint256 chainId;
Currency currency;
uint256 totalBought;
BoughtOrder[] orders;
}
struct FinishOrderExecutionParams {
OrderLib.OrderId[] orderIds;
uint256[] idIndices;
uint256[] pendingOrderCounts;
}
struct ExecuteOrderParams {
OrderLib.OrderId orderId;
uint96 sell;
TradeParams tradeParams;
bytes payload;
}
event OrderFilled(bytes32 indexed id, uint256 sold, uint256 bought);
function receiveIncomingOrders(OrderLib.Order[] calldata orders, Currency currency, uint256 amount) external;
function removeDustOrders(uint256 _incomingOrders) external;
function setOrders(uint256 _incomingOrders, OrderLib.Order[] calldata orders) external;
/// @notice Execute the given local order
/// @param params Execute order data
function executeOrder(ExecuteOrderParams calldata params) external;
function finishOrderExecution(FinishOrderExecutionParams calldata params)
external
returns (PendingOrder[] memory pendingOrders);
function updateFundManager(address fundManager, bool isAllowed) external;
function setMessenger(address messenger) external;
function setPriceOracle(address priceOracle) external;
function setMaxSlippageInBP(uint16 maxSlippageInBP) external;
function orderOf(OrderLib.OrderId calldata orderIdParams) external view returns (uint96 amount);
}
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.23;
interface IPhutureOnDonationCallback {
function phutureOnDonationCallbackV1(bytes calldata data) external;
}
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.23;
import {Currency} from "../libraries/CurrencyLib.sol";
import {a160u96} from "../utils/a160u96.sol";
interface IVault {
struct CurrencyWithdrawal {
uint256[] currencyIndexSet;
uint96[] amounts;
}
struct SnapshotAnatomy {
a160u96[] currencies;
uint256[] currencyIndexSet;
}
struct EndRebalancingParams {
a160u96[] anatomyCurrencies;
SnapshotAnatomy newAnatomy;
CurrencyWithdrawal withdrawals;
uint256 lastKBalance;
Currency[] currencies;
}
struct RebalancingResult {
uint256 chainId;
uint256 snapshot;
uint256[] currencyIdSet;
a160u96[] currencies;
}
struct RegisterCurrenciesResult {
Currency[] currencies;
bytes32 currenciesHash;
}
function setOrderBook(address _orderBook) external;
function setMessenger(address _messenger) external;
function startRebalancingPhase(CurrencyWithdrawal calldata withdrawals) external;
function finishRebalancingPhase(EndRebalancingParams calldata params) external returns (bytes32);
function transferLatestSnapshot(address recipient, uint256 kAmountWads) external returns (uint256);
function withdraw(uint256 snapshot, uint256 kAmount, address recipient) external;
function registerCurrencies(Currency[] calldata currencies) external returns (RegisterCurrenciesResult memory);
function donate(Currency currency, bytes memory data) external;
function consume(Currency currency, uint96 amount, address target, bytes calldata data) external;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;
import {IERC20, SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
type Currency is address;
using {eq as ==, neq as !=} for Currency global;
function eq(Currency currency, Currency other) pure returns (bool) {
return Currency.unwrap(currency) == Currency.unwrap(other);
}
function neq(Currency currency, Currency other) pure returns (bool) {
return !eq(currency, other);
}
/// @title CurrencyLibrary
/// @dev This library allows for transferring and holding native tokens and ERC20 tokens
/// @author Modified from Uniswap (https://github.com/Uniswap/v4-core/blob/main/src/types/Currency.sol)
library CurrencyLib {
using SafeERC20 for IERC20;
using CurrencyLib for Currency;
/// @dev Currency wrapper for native currency
Currency public constant NATIVE = Currency.wrap(address(0));
/// @notice Thrown when a native transfer fails
error NativeTransferFailed();
/// @notice Thrown when an ERC20 transfer fails
error ERC20TransferFailed();
/// @notice Thrown when deposit amount exceeds current balance
error AmountExceedsBalance();
/// @notice Transfers currency
///
/// @param currency Currency to transfer
/// @param to Address of recipient
/// @param amount Currency amount ot transfer
function transfer(Currency currency, address to, uint256 amount) internal {
if (amount == 0) return;
// implementation from
// https://github.com/transmissions11/solmate/blob/e8f96f25d48fe702117ce76c79228ca4f20206cb/src/utils/SafeTransferLib.sol
bool success;
if (currency.isNative()) {
assembly {
// Transfer the ETH and store if it succeeded or not.
success := call(gas(), to, amount, 0, 0, 0, 0)
}
if (!success) revert NativeTransferFailed();
} else {
assembly {
// We'll write our calldata to this slot below, but restore it later.
let freeMemoryPointer := mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.
mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.
success :=
and(
// Set success to whether the call reverted, if not we check it either
// returned exactly 1 (can't just be non-zero data), or had no return data.
or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
// We use 68 because that's the total length of our calldata (4 + 32 * 2)
// Counterintuitively, this call() must be positioned after the or() in the
// surrounding and() because and() evaluates its arguments from right to left.
call(gas(), currency, 0, freeMemoryPointer, 68, 0, 32)
)
}
if (!success) revert ERC20TransferFailed();
}
}
/// @notice Approves currency
///
/// @param currency Currency to approve
/// @param spender Address of spender
/// @param amount Currency amount to approve
function approve(Currency currency, address spender, uint256 amount) internal {
if (isNative(currency)) return;
IERC20(Currency.unwrap(currency)).forceApprove(spender, amount);
}
/// @notice Returns the balance of a given currency for a specific account
///
/// @param currency The currency to check
/// @param account The address of the account
///
/// @return The balance of the specified currency for the given account
function balanceOf(Currency currency, address account) internal view returns (uint256) {
return currency.isNative() ? account.balance : IERC20(Currency.unwrap(currency)).balanceOf(account);
}
/// @notice Returns the balance of a given currency for this contract
///
/// @param currency The currency to check
///
/// @return The balance of the specified currency for this contract
function balanceOfSelf(Currency currency) internal view returns (uint256) {
return currency.isNative() ? address(this).balance : IERC20(Currency.unwrap(currency)).balanceOf(address(this));
}
/// @notice Checks if the specified currency is the native currency
///
/// @param currency The currency to check
///
/// @return `true` if the specified currency is the native currency, `false` otherwise
function isNative(Currency currency) internal pure returns (bool) {
return currency == NATIVE;
}
}
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
/// @notice Arithmetic library with operations for fixed-point numbers.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/FixedPointMathLib.sol)
/// @author Inspired by USM (https://github.com/usmfum/USM/blob/master/contracts/WadMath.sol)
library FixedPointMathLib {
/*//////////////////////////////////////////////////////////////
SIMPLIFIED FIXED POINT OPERATIONS
//////////////////////////////////////////////////////////////*/
uint256 internal constant MAX_UINT256 = 2**256 - 1;
uint256 internal constant WAD = 1e18; // The scalar of ETH and most ERC20s.
function mulWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivDown(x, y, WAD); // Equivalent to (x * y) / WAD rounded down.
}
function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivUp(x, y, WAD); // Equivalent to (x * y) / WAD rounded up.
}
function divWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivDown(x, WAD, y); // Equivalent to (x * WAD) / y rounded down.
}
function divWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivUp(x, WAD, y); // Equivalent to (x * WAD) / y rounded up.
}
/*//////////////////////////////////////////////////////////////
LOW LEVEL FIXED POINT OPERATIONS
//////////////////////////////////////////////////////////////*/
function mulDivDown(
uint256 x,
uint256 y,
uint256 denominator
) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))
if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
revert(0, 0)
}
// Divide x * y by the denominator.
z := div(mul(x, y), denominator)
}
}
function mulDivUp(
uint256 x,
uint256 y,
uint256 denominator
) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))
if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
revert(0, 0)
}
// If x * y modulo the denominator is strictly greater than 0,
// 1 is added to round up the division of x * y by the denominator.
z := add(gt(mod(mul(x, y), denominator), 0), div(mul(x, y), denominator))
}
}
function rpow(
uint256 x,
uint256 n,
uint256 scalar
) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
switch x
case 0 {
switch n
case 0 {
// 0 ** 0 = 1
z := scalar
}
default {
// 0 ** n = 0
z := 0
}
}
default {
switch mod(n, 2)
case 0 {
// If n is even, store scalar in z for now.
z := scalar
}
default {
// If n is odd, store x in z for now.
z := x
}
// Shifting right by 1 is like dividing by 2.
let half := shr(1, scalar)
for {
// Shift n right by 1 before looping to halve it.
n := shr(1, n)
} n {
// Shift n right by 1 each iteration to halve it.
n := shr(1, n)
} {
// Revert immediately if x ** 2 would overflow.
// Equivalent to iszero(eq(div(xx, x), x)) here.
if shr(128, x) {
revert(0, 0)
}
// Store x squared.
let xx := mul(x, x)
// Round to the nearest number.
let xxRound := add(xx, half)
// Revert if xx + half overflowed.
if lt(xxRound, xx) {
revert(0, 0)
}
// Set x to scaled xxRound.
x := div(xxRound, scalar)
// If n is even:
if mod(n, 2) {
// Compute z * x.
let zx := mul(z, x)
// If z * x overflowed:
if iszero(eq(div(zx, x), z)) {
// Revert if x is non-zero.
if iszero(iszero(x)) {
revert(0, 0)
}
}
// Round to the nearest number.
let zxRound := add(zx, half)
// Revert if zx + half overflowed.
if lt(zxRound, zx) {
revert(0, 0)
}
// Return properly scaled zxRound.
z := div(zxRound, scalar)
}
}
}
}
}
/*//////////////////////////////////////////////////////////////
GENERAL NUMBER UTILITIES
//////////////////////////////////////////////////////////////*/
function sqrt(uint256 x) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
let y := x // We start y at x, which will help us make our initial estimate.
z := 181 // The "correct" value is 1, but this saves a multiplication later.
// This segment is to get a reasonable initial estimate for the Babylonian method. With a bad
// start, the correct # of bits increases ~linearly each iteration instead of ~quadratically.
// We check y >= 2^(k + 8) but shift right by k bits
// each branch to ensure that if x >= 256, then y >= 256.
if iszero(lt(y, 0x10000000000000000000000000000000000)) {
y := shr(128, y)
z := shl(64, z)
}
if iszero(lt(y, 0x1000000000000000000)) {
y := shr(64, y)
z := shl(32, z)
}
if iszero(lt(y, 0x10000000000)) {
y := shr(32, y)
z := shl(16, z)
}
if iszero(lt(y, 0x1000000)) {
y := shr(16, y)
z := shl(8, z)
}
// Goal was to get z*z*y within a small factor of x. More iterations could
// get y in a tighter range. Currently, we will have y in [256, 256*2^16).
// We ensured y >= 256 so that the relative difference between y and y+1 is small.
// That's not possible if x < 256 but we can just verify those cases exhaustively.
// Now, z*z*y <= x < z*z*(y+1), and y <= 2^(16+8), and either y >= 256, or x < 256.
// Correctness can be checked exhaustively for x < 256, so we assume y >= 256.
// Then z*sqrt(y) is within sqrt(257)/sqrt(256) of sqrt(x), or about 20bps.
// For s in the range [1/256, 256], the estimate f(s) = (181/1024) * (s+1) is in the range
// (1/2.84 * sqrt(s), 2.84 * sqrt(s)), with largest error when s = 1 and when s = 256 or 1/256.
// Since y is in [256, 256*2^16), let a = y/65536, so that a is in [1/256, 256). Then we can estimate
// sqrt(y) using sqrt(65536) * 181/1024 * (a + 1) = 181/4 * (y + 65536)/65536 = 181 * (y + 65536)/2^18.
// There is no overflow risk here since y < 2^136 after the first branch above.
z := shr(18, mul(z, add(y, 65536))) // A mul() is saved from starting z at 181.
// Given the worst case multiplicative error of 2.84 above, 7 iterations should be enough.
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
// If x+1 is a perfect square, the Babylonian method cycles between
// floor(sqrt(x)) and ceil(sqrt(x)). This statement ensures we return floor.
// See: https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division
// Since the ceil is rare, we save gas on the assignment and repeat division in the rare case.
// If you don't care whether the floor or ceil square root is returned, you can remove this statement.
z := sub(z, lt(div(x, z), z))
}
}
function unsafeMod(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// Mod x by y. Note this will return
// 0 instead of reverting if y is zero.
z := mod(x, y)
}
}
function unsafeDiv(uint256 x, uint256 y) internal pure returns (uint256 r) {
/// @solidity memory-safe-assembly
assembly {
// Divide x by y. Note this will return
// 0 instead of reverting if y is zero.
r := div(x, y)
}
}
function unsafeDivUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// Add 1 to x * y if x % y > 0. Note this will
// return 0 instead of reverting if y is zero.
z := add(gt(mod(x, y), 0), div(x, y))
}
}
}
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.23;
import {Currency} from "./CurrencyLib.sol";
/// @title OrderLib
/// @notice Library for managing orders
library OrderLib {
struct OrderId {
// Sell currency of order
Currency sellCurrency;
// Local buy currency of order
Currency localBuyCurrency;
// Final destination buy currency of order
Currency finalDestinationBuyCurrency;
// Final destination chainId of order
uint256 finalDestinationChainId;
}
struct Order {
// Sell amount of order
uint96 sellAmount;
// Id params of order
OrderId idParams;
}
struct OrderRegistry {
// Sell amount of given order
mapping(bytes32 => uint96) orderOf;
// Hash of registry state
bytes32 ordersHash;
}
/// @notice Emitted when a new order is created
event NewOrder(
uint256 sellAmount,
Currency indexed sellCurrency,
Currency localBuyCurrency,
Currency indexed finalDestinationBuyCurrency,
uint256 finalDestinationChainId
);
/// @notice Thrown when there's a mismatch in order hashes
error OrderHashMismatch();
/// @notice Thrown when an order is not filled
/// @param id The id of the order that was not filled
error OrderNotFilled(bytes32 id);
/// @notice Sets multiple orders in the registry
///
/// @param self The order registry where orders are stored
/// @param orders An array of orders to set in the registry
function set(OrderRegistry storage self, Order[] calldata orders) internal {
bytes32 newHash = self.ordersHash;
for (uint256 i; i < orders.length; ++i) {
if (orders[i].sellAmount == 0) continue;
OrderId calldata params = orders[i].idParams;
// don't need to create order for the same currency within a single chain, as it's already in the final destination
if (params.sellCurrency != params.localBuyCurrency || params.finalDestinationChainId != block.chainid) {
bytes32 idKey = id(params);
newHash = keccak256(abi.encode(newHash, idKey));
self.orderOf[idKey] += orders[i].sellAmount;
emit NewOrder(
orders[i].sellAmount,
params.sellCurrency,
params.localBuyCurrency,
params.finalDestinationBuyCurrency,
params.finalDestinationChainId
);
}
}
self.ordersHash = newHash;
}
/// @notice Fills a specific order from the registry
///
/// @param self The order registry
/// @param orderId The id params of the order to fill
/// @param sell The sell amount of the order
function fill(OrderRegistry storage self, OrderId calldata orderId, uint96 sell) internal {
self.orderOf[id(orderId)] -= sell;
}
/// @notice Resets the orders in the registry
///
/// @param self The order registry to reset
///
/// @param orderIds An array of order id parameters to reset
function reset(OrderRegistry storage self, OrderId[] calldata orderIds) internal {
bytes32 ordersHash;
for (uint256 i; i < orderIds.length; ++i) {
bytes32 idKey = id(orderIds[i]);
ordersHash = keccak256(abi.encode(ordersHash, idKey));
if (self.orderOf[idKey] != 0) revert OrderNotFilled(idKey);
}
if (ordersHash != self.ordersHash) revert OrderHashMismatch();
self.ordersHash = bytes32(0);
}
/// @notice Retrieves the sell amount of a specific order
/// @param self The order registry
///
/// @param orderId The id parameters of the order to retrieve
///
/// @return The sell amount of the specified order
function get(OrderRegistry storage self, OrderId calldata orderId) internal view returns (uint96) {
return self.orderOf[id(orderId)];
}
/// @dev Generates a unique id for an order based on its parameters
///
/// @param self The order id parameters
///
/// @return A unique bytes32 id for the order
function id(OrderId calldata self) internal pure returns (bytes32) {
return keccak256(abi.encode(self));
}
}
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.23;
import {FixedPointMathLib} from "solmate/utils/FixedPointMathLib.sol";
/// @title PriceLib
/// @notice A library for handling fixed-point arithmetic for prices
library PriceLib {
using FixedPointMathLib for uint256;
/// @dev 2**128
uint256 internal constant Q128 = 0x100000000000000000000000000000000;
uint16 internal constant PRICE_ORACLE_DECIMALS = 18;
uint256 internal constant DECIMALS_MULTIPLIER = 10 ** PRICE_ORACLE_DECIMALS;
/// @notice Converts (down) an amount in base units to an amount in asset units based on a fixed-price value
/// @param base The amount to convert in base units
/// @param price The fixed-price value represented as a uint256
/// @return The equivalent amount in asset units
function convertToAssetsDown(uint256 base, uint256 price) internal pure returns (uint256) {
return base.mulDivDown(price, Q128);
}
/// @notice Converts (up) an amount in base units to an amount in asset units based on a fixed-price value
/// @param base The amount to convert in base units
/// @param price The fixed-price value represented as a uint256
/// @return The equivalent amount in asset units
function convertToAssetsUp(uint256 base, uint256 price) internal pure returns (uint256) {
return base.mulDivUp(price, Q128);
}
/// @notice Converts (down) an amount in asset units to an amount in base units based on a fixed-price value
/// @param assets The amount to convert in asset units
/// @param price The fixed-price value represented as a uint256
/// @return The equivalent amount in base units
function convertToBaseDown(uint256 assets, uint256 price) internal pure returns (uint256) {
return assets.mulDivDown(Q128, price);
}
/// @notice Converts (up) an amount in asset units to an amount in base units based on a fixed-price value
/// @param assets The amount to convert in asset units
/// @param price The fixed-price value represented as a uint256
/// @return The equivalent amount in base units
function convertToBaseUp(uint256 assets, uint256 price) internal pure returns (uint256) {
return assets.mulDivUp(Q128, price);
}
}
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
/// @notice Safe unsigned integer casting library that reverts on overflow.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeCastLib.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/math/SafeCast.sol)
library SafeCastLib {
function safeCastTo248(uint256 x) internal pure returns (uint248 y) {
require(x < 1 << 248);
y = uint248(x);
}
function safeCastTo240(uint256 x) internal pure returns (uint240 y) {
require(x < 1 << 240);
y = uint240(x);
}
function safeCastTo232(uint256 x) internal pure returns (uint232 y) {
require(x < 1 << 232);
y = uint232(x);
}
function safeCastTo224(uint256 x) internal pure returns (uint224 y) {
require(x < 1 << 224);
y = uint224(x);
}
function safeCastTo216(uint256 x) internal pure returns (uint216 y) {
require(x < 1 << 216);
y = uint216(x);
}
function safeCastTo208(uint256 x) internal pure returns (uint208 y) {
require(x < 1 << 208);
y = uint208(x);
}
function safeCastTo200(uint256 x) internal pure returns (uint200 y) {
require(x < 1 << 200);
y = uint200(x);
}
function safeCastTo192(uint256 x) internal pure returns (uint192 y) {
require(x < 1 << 192);
y = uint192(x);
}
function safeCastTo184(uint256 x) internal pure returns (uint184 y) {
require(x < 1 << 184);
y = uint184(x);
}
function safeCastTo176(uint256 x) internal pure returns (uint176 y) {
require(x < 1 << 176);
y = uint176(x);
}
function safeCastTo168(uint256 x) internal pure returns (uint168 y) {
require(x < 1 << 168);
y = uint168(x);
}
function safeCastTo160(uint256 x) internal pure returns (uint160 y) {
require(x < 1 << 160);
y = uint160(x);
}
function safeCastTo152(uint256 x) internal pure returns (uint152 y) {
require(x < 1 << 152);
y = uint152(x);
}
function safeCastTo144(uint256 x) internal pure returns (uint144 y) {
require(x < 1 << 144);
y = uint144(x);
}
function safeCastTo136(uint256 x) internal pure returns (uint136 y) {
require(x < 1 << 136);
y = uint136(x);
}
function safeCastTo128(uint256 x) internal pure returns (uint128 y) {
require(x < 1 << 128);
y = uint128(x);
}
function safeCastTo120(uint256 x) internal pure returns (uint120 y) {
require(x < 1 << 120);
y = uint120(x);
}
function safeCastTo112(uint256 x) internal pure returns (uint112 y) {
require(x < 1 << 112);
y = uint112(x);
}
function safeCastTo104(uint256 x) internal pure returns (uint104 y) {
require(x < 1 << 104);
y = uint104(x);
}
function safeCastTo96(uint256 x) internal pure returns (uint96 y) {
require(x < 1 << 96);
y = uint96(x);
}
function safeCastTo88(uint256 x) internal pure returns (uint88 y) {
require(x < 1 << 88);
y = uint88(x);
}
function safeCastTo80(uint256 x) internal pure returns (uint80 y) {
require(x < 1 << 80);
y = uint80(x);
}
function safeCastTo72(uint256 x) internal pure returns (uint72 y) {
require(x < 1 << 72);
y = uint72(x);
}
function safeCastTo64(uint256 x) internal pure returns (uint64 y) {
require(x < 1 << 64);
y = uint64(x);
}
function safeCastTo56(uint256 x) internal pure returns (uint56 y) {
require(x < 1 << 56);
y = uint56(x);
}
function safeCastTo48(uint256 x) internal pure returns (uint48 y) {
require(x < 1 << 48);
y = uint48(x);
}
function safeCastTo40(uint256 x) internal pure returns (uint40 y) {
require(x < 1 << 40);
y = uint40(x);
}
function safeCastTo32(uint256 x) internal pure returns (uint32 y) {
require(x < 1 << 32);
y = uint32(x);
}
function safeCastTo24(uint256 x) internal pure returns (uint24 y) {
require(x < 1 << 24);
y = uint24(x);
}
function safeCastTo16(uint256 x) internal pure returns (uint16 y) {
require(x < 1 << 16);
y = uint16(x);
}
function safeCastTo8(uint256 x) internal pure returns (uint8 y) {
require(x < 1 << 8);
y = uint8(x);
}
}
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
/// @notice Simple single owner authorization mixin.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/Owned.sol)
abstract contract Owned {
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
event OwnershipTransferred(address indexed user, address indexed newOwner);
/*//////////////////////////////////////////////////////////////
OWNERSHIP STORAGE
//////////////////////////////////////////////////////////////*/
address public owner;
modifier onlyOwner() virtual {
require(msg.sender == owner, "UNAUTHORIZED");
_;
}
/*//////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/
constructor(address _owner) {
owner = _owner;
emit OwnershipTransferred(address(0), _owner);
}
/*//////////////////////////////////////////////////////////////
OWNERSHIP LOGIC
//////////////////////////////////////////////////////////////*/
function transferOwnership(address newOwner) public virtual onlyOwner {
owner = newOwner;
emit OwnershipTransferred(msg.sender, newOwner);
}
}
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.23;
import {Currency} from "../libraries/CurrencyLib.sol";
type a160u96 is uint256;
using {addr, unpack, unpackRaw, currency, value, eq as ==, neq as !=} for a160u96 global;
error AddressMismatch(address, address);
function neq(a160u96 a, a160u96 b) pure returns (bool) {
return !eq(a, b);
}
function eq(a160u96 a, a160u96 b) pure returns (bool) {
return a160u96.unwrap(a) == a160u96.unwrap(b);
}
function currency(a160u96 packed) pure returns (Currency) {
return Currency.wrap(addr(packed));
}
function addr(a160u96 packed) pure returns (address) {
return address(uint160(a160u96.unwrap(packed)));
}
function value(a160u96 packed) pure returns (uint96) {
return uint96(a160u96.unwrap(packed) >> 160);
}
function unpack(a160u96 packed) pure returns (Currency _curr, uint96 _value) {
uint256 raw = a160u96.unwrap(packed);
_curr = Currency.wrap(address(uint160(raw)));
_value = uint96(raw >> 160);
}
function unpackRaw(a160u96 packed) pure returns (address _addr, uint96 _value) {
uint256 raw = a160u96.unwrap(packed);
_addr = address(uint160(raw));
_value = uint96(raw >> 160);
}
library A160U96Factory {
function create(address _addr, uint96 _value) internal pure returns (a160u96) {
return a160u96.wrap((uint256(_value) << 160) | uint256(uint160(_addr)));
}
function create(Currency _currency, uint96 _value) internal pure returns (a160u96) {
return create(Currency.unwrap(_currency), _value);
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.3) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.0;
import "../IERC20.sol";
import "../extensions/IERC20Permit.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;
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
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));
}
/**
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
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");
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
* to be set to zero before setting it to a non-zero value, such as USDT.
*/
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
* Revert on invalid signature.
*/
function safePermit(
IERC20Permit token,
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) internal {
uint256 nonceBefore = token.nonces(owner);
token.permit(owner, spender, value, deadline, v, r, s);
uint256 nonceAfter = token.nonces(owner);
require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
}
/**
* @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");
require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
/**
* @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).
*
* This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
// 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 cannot use {Address-functionCall} here since this should return false
// and not revert is the subcall reverts.
(bool success, bytes memory returndata) = address(token).call(data);
return
success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token));
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.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 (last updated v4.9.0) (token/ERC20/extensions/IERC20Permit.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
* presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
* need to send a transaction, and thus is not required to hold Ether at all.
*/
interface IERC20Permit {
/**
* @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
* given ``owner``'s signed approval.
*
* IMPORTANT: The same issues {IERC20-approve} has related to transaction
* ordering also apply here.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `deadline` must be a timestamp in the future.
* - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
* over the EIP712-formatted function arguments.
* - the signature must use ``owner``'s current nonce (see {nonces}).
*
* For more information on the signature format, see the
* https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
* section].
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @dev Returns the current nonce for `owner`. This value must be
* included whenever a signature is generated for {permit}.
*
* Every successful call to {permit} increases ``owner``'s nonce by one. This
* prevents a signature from being used multiple times.
*/
function nonces(address owner) external view returns (uint256);
/**
* @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view returns (bytes32);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)
pragma solidity ^0.8.1;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
*
* Furthermore, `isContract` will also return true if the target contract within
* the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
* which only has an effect at the end of a transaction.
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0
// for contracts in construction, since the code is only stored at the end
// of the constructor execution.
return account.code.length > 0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
* the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
*
* _Available since v4.8._
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
// only check isContract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
/**
* @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason or using the provided one.
*
* _Available since v4.3._
*/
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}