Source Code
Overview
MON Balance
MON Value
$0.00Latest 25 from a total of 108 transactions
| Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
| Flash Execution ... | 50892968 | 1 hr ago | IN | 0.01248646 MON | 0.11216436 | ||||
| Flash Execution ... | 50852350 | 6 hrs ago | IN | 0.0118832 MON | 0.11118756 | ||||
| Flash Execution ... | 50827072 | 8 hrs ago | IN | 0.05337003 MON | 0.11097152 | ||||
| Flash Execution ... | 50816301 | 10 hrs ago | IN | 0.05592844 MON | 0.11117953 | ||||
| Flash Execution ... | 50793992 | 12 hrs ago | IN | 0.0512346 MON | 0.11117337 | ||||
| Flash Execution ... | 50781435 | 14 hrs ago | IN | 0.0673954 MON | 0.10963337 | ||||
| Flash Execution ... | 50780398 | 14 hrs ago | IN | 0.01981924 MON | 0.11116996 | ||||
| Flash Execution ... | 50778116 | 14 hrs ago | IN | 0.02793033 MON | 0.12667149 | ||||
| Flash Execution ... | 50773525 | 14 hrs ago | IN | 0.07251589 MON | 0.11108394 | ||||
| Flash Execution ... | 50767349 | 15 hrs ago | IN | 0.12942189 MON | 0.10855691 | ||||
| Flash Execution ... | 50764181 | 15 hrs ago | IN | 0.06375754 MON | 0.10939093 | ||||
| Flash Execution ... | 50762370 | 16 hrs ago | IN | 0.15343903 MON | 0.11105776 | ||||
| Flash Execution ... | 50751033 | 17 hrs ago | IN | 0.01214337 MON | 0.11117381 | ||||
| Flash Execution ... | 50740816 | 18 hrs ago | IN | 0.01920917 MON | 0.11108339 | ||||
| Flash Execution ... | 50660566 | 27 hrs ago | IN | 0.05789249 MON | 0.13442715 | ||||
| Flash Execution ... | 50622109 | 31 hrs ago | IN | 0.01178824 MON | 0.11193809 | ||||
| Flash Execution ... | 50567748 | 37 hrs ago | IN | 0.01934906 MON | 0.10029756 | ||||
| Flash Execution ... | 50547933 | 40 hrs ago | IN | 0.11406732 MON | 0.10833702 | ||||
| Flash Execution ... | 50520764 | 43 hrs ago | IN | 0.01967332 MON | 0.10802759 | ||||
| Flash Execution ... | 50405457 | 2 days ago | IN | 0.01548159 MON | 0.10952535 | ||||
| Flash Execution ... | 50392148 | 2 days ago | IN | 0.01147231 MON | 0.11085173 | ||||
| Flash Execution ... | 50392085 | 2 days ago | IN | 0.02122039 MON | 0.10939533 | ||||
| Flash Execution ... | 50391789 | 2 days ago | IN | 0.01432613 MON | 0.1084699 | ||||
| Flash Execution ... | 50391104 | 2 days ago | IN | 0.01387609 MON | 0.10813517 | ||||
| Flash Execution ... | 50386526 | 2 days ago | IN | 0.01804206 MON | 0.10843602 |
Latest 25 internal transactions (View All)
Advanced mode:
| Parent Transaction Hash | Block | From | To | |||
|---|---|---|---|---|---|---|
| 50892968 | 1 hr ago | 0.01248646 MON | ||||
| 50892968 | 1 hr ago | 0.01248646 MON | ||||
| 50892968 | 1 hr ago | 0.01248646 MON | ||||
| 50852350 | 6 hrs ago | 0.0118832 MON | ||||
| 50852350 | 6 hrs ago | 0.0118832 MON | ||||
| 50852350 | 6 hrs ago | 0.0118832 MON | ||||
| 50827072 | 8 hrs ago | 0.05337003 MON | ||||
| 50827072 | 8 hrs ago | 0.05337003 MON | ||||
| 50827072 | 8 hrs ago | 0.05337003 MON | ||||
| 50816301 | 10 hrs ago | 0.05592844 MON | ||||
| 50816301 | 10 hrs ago | 0.05592844 MON | ||||
| 50816301 | 10 hrs ago | 0.05592844 MON | ||||
| 50793992 | 12 hrs ago | 0.0512346 MON | ||||
| 50793992 | 12 hrs ago | 0.0512346 MON | ||||
| 50793992 | 12 hrs ago | 0.0512346 MON | ||||
| 50781435 | 14 hrs ago | 0.0673954 MON | ||||
| 50781435 | 14 hrs ago | 0.0673954 MON | ||||
| 50781435 | 14 hrs ago | 0.0673954 MON | ||||
| 50780398 | 14 hrs ago | 0.01981924 MON | ||||
| 50780398 | 14 hrs ago | 0.01981924 MON | ||||
| 50780398 | 14 hrs ago | 0.01981924 MON | ||||
| 50778116 | 14 hrs ago | 0.02793033 MON | ||||
| 50778116 | 14 hrs ago | 0.02793033 MON | ||||
| 50778116 | 14 hrs ago | 0.02793033 MON | ||||
| 50773525 | 14 hrs ago | 0.07251589 MON |
Loading...
Loading
Contract Name:
FastLaneAuctionHandler
Compiler Version
v0.8.28+commit.7893614a
Optimization Enabled:
Yes with 50 runs
Other Settings:
cancun EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import { SafeTransferLib } from "@solady/utils/SafeTransferLib.sol";
import { SafeCastLib } from "@solady/utils/SafeCastLib.sol";
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { IShMonad } from "@fastlane/contracts/shmonad/interfaces/IShMonad.sol";
import { IMonadStaking } from "@fastlane/contracts/shmonad/interfaces/IMonadStaking.sol";
import { FastLaneAuctionHandlerEvents } from "./AuctionEvents.sol";
import { FastLaneAuctionHandlerErrors } from "./AuctionErrors.sol";
import { BidType, BidData, ISearcherContract } from "./Types.sol";
import { COMMIT_DURATION, PRECOMPILE_MONAD_STAKING, PROTOCOL_FEE_RATE } from "./Constants.sol";
using SafeTransferLib for address;
using SafeCastLib for uint256;
contract FastLaneAuctionHandler is FastLaneAuctionHandlerEvents, FastLaneAuctionHandlerErrors, Ownable {
/// @notice The shMonad contract address
address public immutable SHMONAD;
/// @notice The policy ID for the shMonad commitment
uint64 public immutable POLICY_ID;
/// @notice Map key is either target block number for ToB or target tx hash for bundle
mapping(bytes32 => BidData) public bids;
bytes32 private constant UNLOCKED = bytes32(uint256(1));
bytes32 private lock = UNLOCKED;
constructor(address _shMonad) Ownable(msg.sender) {
require(_shMonad != address(0), RelayCannotBeZero());
SHMONAD = _shMonad;
(uint64 policyID) = IShMonad(_shMonad).createPolicy(COMMIT_DURATION);
POLICY_ID = policyID;
}
/// @notice Submits a flash bid
/// @dev Will revert if: already won, minimum bid not respected, or not from EOA
/// @param bidAmount Amount committed to be repaid
/// @param txHashes Target Transaction hashes
/// @param targetBlockNumber Target block number
/// @param executeOnLoss If true, execute even if searcher lost auction
/// @param payBidOnFail bid paid through committed shmon
/// @param searcherToAddress Searcher contract address to be called on its `fastLaneCall` function
/// @param searcherCallData callData to be passed to
/// `_searcherToAddress.fastLaneCall(_bidAmount,msg.sender,callData)`
function flashExecutionBid(
uint256 bidAmount,
bytes32[] calldata txHashes,
uint256 targetBlockNumber,
bool executeOnLoss,
bool payBidOnFail,
address searcherToAddress,
bytes calldata searcherCallData
)
external
payable
nonReentrant
{
require(targetBlockNumber >= block.number, RelayTargetBlockNumberExceeded());
require(bidAmount > 0, RelayBidAmountIsZero());
BidData memory _bid = _getValidatedBid(bidAmount, txHashes, executeOnLoss);
if (_bid.executable) {
try this.bidWrapper{ value: msg.value }(
msg.sender, bidAmount, payBidOnFail, searcherToAddress, searcherCallData
) {
_markResult(_bid, bidAmount, txHashes, true);
} catch (bytes memory reason) {
emit RelayBidFailed(msg.sender, reason);
_markResult(_bid, bidAmount, txHashes, false);
if (msg.value > 0) msg.sender.safeTransferETH(msg.value);
}
} else {
_markResult(_bid, bidAmount, txHashes, false);
if (msg.value > 0) msg.sender.safeTransferETH(msg.value);
}
}
function bidWrapper(
address msgSender,
uint256 bidAmount, // Value commited to be paid at the end of execution
bool payBidOnFail,
address searcherToAddress,
bytes calldata searcherCallData
)
external
payable
{
// This is meant to be called inside of a try/catch by address(this)
require(msg.sender == address(this), RelayMustBeSelf());
// Store the current balance, excluding msg.value, and store the gas left
uint256 _balanceBefore = address(this).balance - msg.value;
// Call the searcher's contract (see searcher_contract.sol for example of call receiver)
// And forward msg.value
(bool _success, bytes memory _retData) = ISearcherContract(searcherToAddress).fastLaneCall{ value: msg.value }(
msgSender, bidAmount, searcherCallData
);
// if searcher failed and payBidOnFail is false, revert
if (!_success && !payBidOnFail) {
assembly {
revert(
// Start of revert data bytes. The 0x20 offset is always the same.
add(_retData, 0x20),
// Length of revert data.
mload(_retData)
)
}
}
// Verify that the searcher paid the amount they bid & emit the event
uint256 _bidReceived = address(this).balance - _balanceBefore;
if (_bidReceived > bidAmount) {
// overpaid, refund the surplus to the searcher
msgSender.safeTransferETH(_bidReceived - bidAmount);
_bidReceived = bidAmount;
}
// bidReceived is less than or equal to bidAmount
_payValidator(msgSender, bidAmount, _bidReceived, payBidOnFail);
}
receive() external payable { }
fallback() external payable { }
/**
* |
* | Internal Bid Helper Functions |
* |__________________________________
*/
function _payValidator(address _msgSender, uint256 _bidAmount, uint256 _bidReceived, bool _payBidOnFail) internal {
if (_payBidOnFail && _bidReceived < _bidAmount) {
IShMonad(SHMONAD)
.agentWithdrawFromCommitted(POLICY_ID, _msgSender, address(this), _bidAmount - _bidReceived, 0, true);
_bidReceived = _bidAmount;
}
require(_bidReceived == _bidAmount, RelayNotRepaid(_bidAmount, _bidReceived));
uint64 validatorId = IMonadStaking(PRECOMPILE_MONAD_STAKING).getProposerValId();
// Send full reward through shmonad; protocol fee taken inside shmonad and MEV rewards are handled there.
try IShMonad(SHMONAD).sendValidatorRewards{ value: _bidAmount }(validatorId, PROTOCOL_FEE_RATE) { }
catch {
SafeTransferLib.safeTransferETH(block.coinbase, _bidAmount);
}
emit RelayFeeCollected(_msgSender, validatorId, _bidAmount);
}
/// @notice Validates incoming bid
/// @dev validates whether bid is for top of block or a bundler.
/// @param bidAmount Amount committed to be repaid
/// @param txHashes Target Transaction hashes
/// @param executeOnLoss If true, execute even if searcher lost auction
function _getValidatedBid(
uint256 bidAmount,
bytes32[] calldata txHashes,
bool executeOnLoss
)
internal
returns (BidData memory)
{
uint256 _txHashesCount = txHashes.length;
require(_txHashesCount > 0, RelayTxHashesCountIsZero());
// For first version, the last tx hash must be the zero hash
// Subject to change in future versions
require(txHashes[_txHashesCount - 1] == bytes32(0), RelayTxHashMustBeZero());
uint128 bidAmount128 = (bidAmount).toUint128();
// top of block
if (_txHashesCount == 1) {
// Get the existing top bid (if one exists)
bytes32 _bidKey = keccak256(abi.encode(BidType.TopOfBlock, block.number));
BidData memory _existingBid = bids[_bidKey];
// Check if this is the highest
if (_existingBid.bidCount == 0) {
return BidData({
amount: bidAmount128,
blockNumber: uint64(block.number),
bidType: BidType.TopOfBlock,
bidCount: 0,
executable: true,
won: false
});
// Check the existing bid
} else {
return BidData({
amount: bidAmount128,
blockNumber: uint64(block.number),
bidType: BidType.TopOfBlock,
bidCount: _existingBid.bidCount,
executable: !_existingBid.won || executeOnLoss,
won: _existingBid.won
});
}
// backrun bundle
} else if (_txHashesCount == 2) {
BidData memory _bid = BidData({
amount: bidAmount128,
blockNumber: uint64(block.number),
bidType: BidType.Bundle,
bidCount: 0,
executable: true,
won: false
});
for (uint256 i; i < _txHashesCount - 1; i++) {
bytes32 _txHash = txHashes[i];
BidData memory _existingBid = bids[_txHash];
if (_existingBid.bidCount == 0) continue;
// Determine if this is executable
if (!_bid.won && _existingBid.won) {
_bid.won = true;
_bid.executable = executeOnLoss;
}
}
return _bid;
} else {
revert RelayTxHashesCountInvalid();
// Will add future orderings in a later version
}
}
function _markResult(BidData memory bid, uint256 bidAmount, bytes32[] calldata txHashes, bool success) internal {
if (bid.bidType == BidType.TopOfBlock) {
// Get the bid key
bytes32 _bidKey = keccak256(abi.encode(BidType.TopOfBlock, block.number));
BidData memory _existingBid = bids[_bidKey];
// only update the existing bid if nobody won yet
if (!_existingBid.won) {
_existingBid = bid;
_existingBid.won = success;
}
_existingBid.bidCount += 1;
// Store the updated bid data
bids[_bidKey] = _existingBid;
} else {
uint256 _txHashesCount = txHashes.length;
for (uint256 i; i < _txHashesCount - 1; i++) {
bytes32 _txHash = txHashes[i];
BidData memory _existingBid = bids[_txHash];
// only update the existing bid if nobody won yet
if (!_existingBid.won) {
_existingBid = bid;
_existingBid.won = success;
}
_existingBid.bidCount += 1;
// Store the updated bid data
bids[_txHash] = _existingBid;
}
}
bid.won = success;
}
/**
* |
* | Maintenance |
* |__________________________________
*/
/// @notice Syncs stuck monad to calling validator
/// @dev In the event something went really wrong / vuln report
function withdrawStuckNativeToken() external onlyOwner nonReentrant {
uint256 _currentBalance = address(this).balance;
if (_currentBalance > 0) {
msg.sender.safeTransferETH(_currentBalance);
emit RelayWithdrawStuckNativeToken(msg.sender, _currentBalance);
}
}
/// @notice Withdraws stuck ERC20
/// @dev In the event people send ERC20 instead of MON we can send them back
/// @param _tokenAddress Address of the stuck token
function withdrawStuckERC20(address _tokenAddress) external onlyOwner nonReentrant {
uint256 oopsTokenBalance = IERC20(_tokenAddress).balanceOf(address(this));
if (oopsTokenBalance > 0) {
SafeTransferLib.safeTransfer(_tokenAddress, msg.sender, oopsTokenBalance);
emit RelayWithdrawStuckERC20(msg.sender, _tokenAddress, oopsTokenBalance);
}
}
/**
* |
* | Modifiers |
* |__________________________________
*/
modifier nonReentrant() {
require(lock == UNLOCKED, RelayUnapprovedReentrancy());
lock = bytes32(uint256(uint160(address(msg.sender))));
_;
lock = UNLOCKED;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC20/ERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "./IERC20.sol";
import {IERC20Metadata} from "./extensions/IERC20Metadata.sol";
import {Context} from "../../utils/Context.sol";
import {IERC20Errors} from "../../interfaces/draft-IERC6093.sol";
/**
* @dev Implementation of the {IERC20} interface.
*
* This implementation is agnostic to the way tokens are created. This means
* that a supply mechanism has to be added in a derived contract using {_mint}.
*
* TIP: For a detailed writeup see our guide
* https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
* to implement supply mechanisms].
*
* The default value of {decimals} is 18. To change this, you should override
* this function so it returns a different value.
*
* We have followed general OpenZeppelin Contracts guidelines: functions revert
* instead returning `false` on failure. This behavior is nonetheless
* conventional and does not conflict with the expectations of ERC-20
* applications.
*/
abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors {
mapping(address account => uint256) private _balances;
mapping(address account => mapping(address spender => uint256)) private _allowances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
/**
* @dev Sets the values for {name} and {symbol}.
*
* Both values are immutable: they can only be set once during construction.
*/
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
/**
* @dev Returns the name of the token.
*/
function name() public view virtual returns (string memory) {
return _name;
}
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() public view virtual returns (string memory) {
return _symbol;
}
/**
* @dev Returns the number of decimals used to get its user representation.
* For example, if `decimals` equals `2`, a balance of `505` tokens should
* be displayed to a user as `5.05` (`505 / 10 ** 2`).
*
* Tokens usually opt for a value of 18, imitating the relationship between
* Ether and Wei. This is the default value returned by this function, unless
* it's overridden.
*
* NOTE: This information is only used for _display_ purposes: it in
* no way affects any of the arithmetic of the contract, including
* {IERC20-balanceOf} and {IERC20-transfer}.
*/
function decimals() public view virtual returns (uint8) {
return 18;
}
/// @inheritdoc IERC20
function totalSupply() public view virtual returns (uint256) {
return _totalSupply;
}
/// @inheritdoc IERC20
function balanceOf(address account) public view virtual returns (uint256) {
return _balances[account];
}
/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - the caller must have a balance of at least `value`.
*/
function transfer(address to, uint256 value) public virtual returns (bool) {
address owner = _msgSender();
_transfer(owner, to, value);
return true;
}
/// @inheritdoc IERC20
function allowance(address owner, address spender) public view virtual returns (uint256) {
return _allowances[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*
* NOTE: If `value` is the maximum `uint256`, the allowance is not updated on
* `transferFrom`. This is semantically equivalent to an infinite approval.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function approve(address spender, uint256 value) public virtual returns (bool) {
address owner = _msgSender();
_approve(owner, spender, value);
return true;
}
/**
* @dev See {IERC20-transferFrom}.
*
* Skips emitting an {Approval} event indicating an allowance update. This is not
* required by the ERC. See {xref-ERC20-_approve-address-address-uint256-bool-}[_approve].
*
* NOTE: Does not update the allowance if the current allowance
* is the maximum `uint256`.
*
* Requirements:
*
* - `from` and `to` cannot be the zero address.
* - `from` must have a balance of at least `value`.
* - the caller must have allowance for ``from``'s tokens of at least
* `value`.
*/
function transferFrom(address from, address to, uint256 value) public virtual returns (bool) {
address spender = _msgSender();
_spendAllowance(from, spender, value);
_transfer(from, to, value);
return true;
}
/**
* @dev Moves a `value` amount of tokens from `from` to `to`.
*
* This internal function is equivalent to {transfer}, and can be used to
* e.g. implement automatic token fees, slashing mechanisms, etc.
*
* Emits a {Transfer} event.
*
* NOTE: This function is not virtual, {_update} should be overridden instead.
*/
function _transfer(address from, address to, uint256 value) internal {
if (from == address(0)) {
revert ERC20InvalidSender(address(0));
}
if (to == address(0)) {
revert ERC20InvalidReceiver(address(0));
}
_update(from, to, value);
}
/**
* @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from`
* (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding
* this function.
*
* Emits a {Transfer} event.
*/
function _update(address from, address to, uint256 value) internal virtual {
if (from == address(0)) {
// Overflow check required: The rest of the code assumes that totalSupply never overflows
_totalSupply += value;
} else {
uint256 fromBalance = _balances[from];
if (fromBalance < value) {
revert ERC20InsufficientBalance(from, fromBalance, value);
}
unchecked {
// Overflow not possible: value <= fromBalance <= totalSupply.
_balances[from] = fromBalance - value;
}
}
if (to == address(0)) {
unchecked {
// Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply.
_totalSupply -= value;
}
} else {
unchecked {
// Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256.
_balances[to] += value;
}
}
emit Transfer(from, to, value);
}
/**
* @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0).
* Relies on the `_update` mechanism
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* NOTE: This function is not virtual, {_update} should be overridden instead.
*/
function _mint(address account, uint256 value) internal {
if (account == address(0)) {
revert ERC20InvalidReceiver(address(0));
}
_update(address(0), account, value);
}
/**
* @dev Destroys a `value` amount of tokens from `account`, lowering the total supply.
* Relies on the `_update` mechanism.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* NOTE: This function is not virtual, {_update} should be overridden instead
*/
function _burn(address account, uint256 value) internal {
if (account == address(0)) {
revert ERC20InvalidSender(address(0));
}
_update(account, address(0), value);
}
/**
* @dev Sets `value` as the allowance of `spender` over the `owner`'s tokens.
*
* This internal function is equivalent to `approve`, and can be used to
* e.g. set automatic allowances for certain subsystems, etc.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
*
* Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument.
*/
function _approve(address owner, address spender, uint256 value) internal {
_approve(owner, spender, value, true);
}
/**
* @dev Variant of {_approve} with an optional flag to enable or disable the {Approval} event.
*
* By default (when calling {_approve}) the flag is set to true. On the other hand, approval changes made by
* `_spendAllowance` during the `transferFrom` operation set the flag to false. This saves gas by not emitting any
* `Approval` event during `transferFrom` operations.
*
* Anyone who wishes to continue emitting `Approval` events on the`transferFrom` operation can force the flag to
* true using the following override:
*
* ```solidity
* function _approve(address owner, address spender, uint256 value, bool) internal virtual override {
* super._approve(owner, spender, value, true);
* }
* ```
*
* Requirements are the same as {_approve}.
*/
function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual {
if (owner == address(0)) {
revert ERC20InvalidApprover(address(0));
}
if (spender == address(0)) {
revert ERC20InvalidSpender(address(0));
}
_allowances[owner][spender] = value;
if (emitEvent) {
emit Approval(owner, spender, value);
}
}
/**
* @dev Updates `owner`'s allowance for `spender` based on spent `value`.
*
* Does not update the allowance value in case of infinite allowance.
* Revert if not enough allowance is available.
*
* Does not emit an {Approval} event.
*/
function _spendAllowance(address owner, address spender, uint256 value) internal virtual {
uint256 currentAllowance = allowance(owner, spender);
if (currentAllowance < type(uint256).max) {
if (currentAllowance < value) {
revert ERC20InsufficientAllowance(spender, currentAllowance, value);
}
unchecked {
_approve(owner, spender, currentAllowance - value, false);
}
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SafeTransferLib.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
/// @author Permit2 operations from (https://github.com/Uniswap/permit2/blob/main/src/libraries/Permit2Lib.sol)
///
/// @dev Note:
/// - For ETH transfers, please use `forceSafeTransferETH` for DoS protection.
library SafeTransferLib {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The ETH transfer has failed.
error ETHTransferFailed();
/// @dev The ERC20 `transferFrom` has failed.
error TransferFromFailed();
/// @dev The ERC20 `transfer` has failed.
error TransferFailed();
/// @dev The ERC20 `approve` has failed.
error ApproveFailed();
/// @dev The ERC20 `totalSupply` query has failed.
error TotalSupplyQueryFailed();
/// @dev The Permit2 operation has failed.
error Permit2Failed();
/// @dev The Permit2 amount must be less than `2**160 - 1`.
error Permit2AmountOverflow();
/// @dev The Permit2 approve operation has failed.
error Permit2ApproveFailed();
/// @dev The Permit2 lockdown operation has failed.
error Permit2LockdownFailed();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTANTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Suggested gas stipend for contract receiving ETH that disallows any storage writes.
uint256 internal constant GAS_STIPEND_NO_STORAGE_WRITES = 2300;
/// @dev Suggested gas stipend for contract receiving ETH to perform a few
/// storage reads and writes, but low enough to prevent griefing.
uint256 internal constant GAS_STIPEND_NO_GRIEF = 100000;
/// @dev The unique EIP-712 domain separator for the DAI token contract.
bytes32 internal constant DAI_DOMAIN_SEPARATOR =
0xdbb8cf42e1ecb028be3f3dbc922e1d878b963f411dc388ced501601c60f7c6f7;
/// @dev The address for the WETH9 contract on Ethereum mainnet.
address internal constant WETH9 = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
/// @dev The canonical Permit2 address.
/// [Github](https://github.com/Uniswap/permit2)
/// [Etherscan](https://etherscan.io/address/0x000000000022D473030F116dDEE9F6B43aC78BA3)
address internal constant PERMIT2 = 0x000000000022D473030F116dDEE9F6B43aC78BA3;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ETH OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
// If the ETH transfer MUST succeed with a reasonable gas budget, use the force variants.
//
// The regular variants:
// - Forwards all remaining gas to the target.
// - Reverts if the target reverts.
// - Reverts if the current contract has insufficient balance.
//
// The force variants:
// - Forwards with an optional gas stipend
// (defaults to `GAS_STIPEND_NO_GRIEF`, which is sufficient for most cases).
// - If the target reverts, or if the gas stipend is exhausted,
// creates a temporary contract to force send the ETH via `SELFDESTRUCT`.
// Future compatible with `SENDALL`: https://eips.ethereum.org/EIPS/eip-4758.
// - Reverts if the current contract has insufficient balance.
//
// The try variants:
// - Forwards with a mandatory gas stipend.
// - Instead of reverting, returns whether the transfer succeeded.
/// @dev Sends `amount` (in wei) ETH to `to`.
function safeTransferETH(address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
if iszero(call(gas(), to, amount, codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Sends all the ETH in the current contract to `to`.
function safeTransferAllETH(address to) internal {
/// @solidity memory-safe-assembly
assembly {
// Transfer all the ETH and check if it succeeded or not.
if iszero(call(gas(), to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Force sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
function forceSafeTransferETH(address to, uint256 amount, uint256 gasStipend) internal {
/// @solidity memory-safe-assembly
assembly {
if lt(selfbalance(), amount) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
revert(0x1c, 0x04)
}
if iszero(call(gasStipend, to, amount, codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, to) // Store the address in scratch space.
mstore8(0x0b, 0x73) // Opcode `PUSH20`.
mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
if iszero(create(amount, 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
}
}
}
/// @dev Force sends all the ETH in the current contract to `to`, with a `gasStipend`.
function forceSafeTransferAllETH(address to, uint256 gasStipend) internal {
/// @solidity memory-safe-assembly
assembly {
if iszero(call(gasStipend, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, to) // Store the address in scratch space.
mstore8(0x0b, 0x73) // Opcode `PUSH20`.
mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
if iszero(create(selfbalance(), 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
}
}
}
/// @dev Force sends `amount` (in wei) ETH to `to`, with `GAS_STIPEND_NO_GRIEF`.
function forceSafeTransferETH(address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
if lt(selfbalance(), amount) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
revert(0x1c, 0x04)
}
if iszero(call(GAS_STIPEND_NO_GRIEF, to, amount, codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, to) // Store the address in scratch space.
mstore8(0x0b, 0x73) // Opcode `PUSH20`.
mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
if iszero(create(amount, 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
}
}
}
/// @dev Force sends all the ETH in the current contract to `to`, with `GAS_STIPEND_NO_GRIEF`.
function forceSafeTransferAllETH(address to) internal {
/// @solidity memory-safe-assembly
assembly {
// forgefmt: disable-next-item
if iszero(call(GAS_STIPEND_NO_GRIEF, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, to) // Store the address in scratch space.
mstore8(0x0b, 0x73) // Opcode `PUSH20`.
mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
if iszero(create(selfbalance(), 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
}
}
}
/// @dev Sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
function trySafeTransferETH(address to, uint256 amount, uint256 gasStipend)
internal
returns (bool success)
{
/// @solidity memory-safe-assembly
assembly {
success := call(gasStipend, to, amount, codesize(), 0x00, codesize(), 0x00)
}
}
/// @dev Sends all the ETH in the current contract to `to`, with a `gasStipend`.
function trySafeTransferAllETH(address to, uint256 gasStipend)
internal
returns (bool success)
{
/// @solidity memory-safe-assembly
assembly {
success := call(gasStipend, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ERC20 OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Sends `amount` of ERC20 `token` from `from` to `to`.
/// Reverts upon failure.
///
/// The `from` account must have at least `amount` approved for
/// the current contract to manage.
function safeTransferFrom(address token, address from, address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x60, amount) // Store the `amount` argument.
mstore(0x40, to) // Store the `to` argument.
mstore(0x2c, shl(96, from)) // Store the `from` argument.
mstore(0x0c, 0x23b872dd000000000000000000000000) // `transferFrom(address,address,uint256)`.
let success := call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
if iszero(and(eq(mload(0x00), 1), success)) {
if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
revert(0x1c, 0x04)
}
}
mstore(0x60, 0) // Restore the zero slot to zero.
mstore(0x40, m) // Restore the free memory pointer.
}
}
/// @dev Sends `amount` of ERC20 `token` from `from` to `to`.
///
/// The `from` account must have at least `amount` approved for the current contract to manage.
function trySafeTransferFrom(address token, address from, address to, uint256 amount)
internal
returns (bool success)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x60, amount) // Store the `amount` argument.
mstore(0x40, to) // Store the `to` argument.
mstore(0x2c, shl(96, from)) // Store the `from` argument.
mstore(0x0c, 0x23b872dd000000000000000000000000) // `transferFrom(address,address,uint256)`.
success := call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
if iszero(and(eq(mload(0x00), 1), success)) {
success := lt(or(iszero(extcodesize(token)), returndatasize()), success)
}
mstore(0x60, 0) // Restore the zero slot to zero.
mstore(0x40, m) // Restore the free memory pointer.
}
}
/// @dev Sends all of ERC20 `token` from `from` to `to`.
/// Reverts upon failure.
///
/// The `from` account must have their entire balance approved for the current contract to manage.
function safeTransferAllFrom(address token, address from, address to)
internal
returns (uint256 amount)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x40, to) // Store the `to` argument.
mstore(0x2c, shl(96, from)) // Store the `from` argument.
mstore(0x0c, 0x70a08231000000000000000000000000) // `balanceOf(address)`.
// Read the balance, reverting upon failure.
if iszero(
and( // The arguments of `and` are evaluated from right to left.
gt(returndatasize(), 0x1f), // At least 32 bytes returned.
staticcall(gas(), token, 0x1c, 0x24, 0x60, 0x20)
)
) {
mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
revert(0x1c, 0x04)
}
mstore(0x00, 0x23b872dd) // `transferFrom(address,address,uint256)`.
amount := mload(0x60) // The `amount` is already at 0x60. We'll need to return it.
// Perform the transfer, reverting upon failure.
let success := call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
if iszero(and(eq(mload(0x00), 1), success)) {
if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
revert(0x1c, 0x04)
}
}
mstore(0x60, 0) // Restore the zero slot to zero.
mstore(0x40, m) // Restore the free memory pointer.
}
}
/// @dev Sends `amount` of ERC20 `token` from the current contract to `to`.
/// Reverts upon failure.
function safeTransfer(address token, address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
mstore(0x14, to) // Store the `to` argument.
mstore(0x34, amount) // Store the `amount` argument.
mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`.
// Perform the transfer, reverting upon failure.
let success := call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
if iszero(and(eq(mload(0x00), 1), success)) {
if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
revert(0x1c, 0x04)
}
}
mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
}
}
/// @dev Sends all of ERC20 `token` from the current contract to `to`.
/// Reverts upon failure.
function safeTransferAll(address token, address to) internal returns (uint256 amount) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, 0x70a08231) // Store the function selector of `balanceOf(address)`.
mstore(0x20, address()) // Store the address of the current contract.
// Read the balance, reverting upon failure.
if iszero(
and( // The arguments of `and` are evaluated from right to left.
gt(returndatasize(), 0x1f), // At least 32 bytes returned.
staticcall(gas(), token, 0x1c, 0x24, 0x34, 0x20)
)
) {
mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
revert(0x1c, 0x04)
}
mstore(0x14, to) // Store the `to` argument.
amount := mload(0x34) // The `amount` is already at 0x34. We'll need to return it.
mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`.
// Perform the transfer, reverting upon failure.
let success := call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
if iszero(and(eq(mload(0x00), 1), success)) {
if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
revert(0x1c, 0x04)
}
}
mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
}
}
/// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract.
/// Reverts upon failure.
function safeApprove(address token, address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
mstore(0x14, to) // Store the `to` argument.
mstore(0x34, amount) // Store the `amount` argument.
mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
let success := call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
if iszero(and(eq(mload(0x00), 1), success)) {
if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`.
revert(0x1c, 0x04)
}
}
mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
}
}
/// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract.
/// If the initial attempt to approve fails, attempts to reset the approved amount to zero,
/// then retries the approval again (some tokens, e.g. USDT, requires this).
/// Reverts upon failure.
function safeApproveWithRetry(address token, address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
mstore(0x14, to) // Store the `to` argument.
mstore(0x34, amount) // Store the `amount` argument.
mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
// Perform the approval, retrying upon failure.
let success := call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
if iszero(and(eq(mload(0x00), 1), success)) {
if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
mstore(0x34, 0) // Store 0 for the `amount`.
mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
pop(call(gas(), token, 0, 0x10, 0x44, codesize(), 0x00)) // Reset the approval.
mstore(0x34, amount) // Store back the original `amount`.
// Retry the approval, reverting upon failure.
success := call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
if iszero(and(eq(mload(0x00), 1), success)) {
// Check the `extcodesize` again just in case the token selfdestructs lol.
if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`.
revert(0x1c, 0x04)
}
}
}
}
mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
}
}
/// @dev Returns the amount of ERC20 `token` owned by `account`.
/// Returns zero if the `token` does not exist.
function balanceOf(address token, address account) internal view returns (uint256 amount) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x14, account) // Store the `account` argument.
mstore(0x00, 0x70a08231000000000000000000000000) // `balanceOf(address)`.
amount :=
mul( // The arguments of `mul` are evaluated from right to left.
mload(0x20),
and( // The arguments of `and` are evaluated from right to left.
gt(returndatasize(), 0x1f), // At least 32 bytes returned.
staticcall(gas(), token, 0x10, 0x24, 0x20, 0x20)
)
)
}
}
/// @dev Performs a `token.balanceOf(account)` check.
/// `implemented` denotes whether the `token` does not implement `balanceOf`.
/// `amount` is zero if the `token` does not implement `balanceOf`.
function checkBalanceOf(address token, address account)
internal
view
returns (bool implemented, uint256 amount)
{
/// @solidity memory-safe-assembly
assembly {
mstore(0x14, account) // Store the `account` argument.
mstore(0x00, 0x70a08231000000000000000000000000) // `balanceOf(address)`.
implemented :=
and( // The arguments of `and` are evaluated from right to left.
gt(returndatasize(), 0x1f), // At least 32 bytes returned.
staticcall(gas(), token, 0x10, 0x24, 0x20, 0x20)
)
amount := mul(mload(0x20), implemented)
}
}
/// @dev Returns the total supply of the `token`.
/// Reverts if the token does not exist or does not implement `totalSupply()`.
function totalSupply(address token) internal view returns (uint256 result) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, 0x18160ddd) // `totalSupply()`.
if iszero(
and(gt(returndatasize(), 0x1f), staticcall(gas(), token, 0x1c, 0x04, 0x00, 0x20))
) {
mstore(0x00, 0x54cd9435) // `TotalSupplyQueryFailed()`.
revert(0x1c, 0x04)
}
result := mload(0x00)
}
}
/// @dev Sends `amount` of ERC20 `token` from `from` to `to`.
/// If the initial attempt fails, try to use Permit2 to transfer the token.
/// Reverts upon failure.
///
/// The `from` account must have at least `amount` approved for the current contract to manage.
function safeTransferFrom2(address token, address from, address to, uint256 amount) internal {
if (!trySafeTransferFrom(token, from, to, amount)) {
permit2TransferFrom(token, from, to, amount);
}
}
/// @dev Sends `amount` of ERC20 `token` from `from` to `to` via Permit2.
/// Reverts upon failure.
function permit2TransferFrom(address token, address from, address to, uint256 amount)
internal
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(add(m, 0x74), shr(96, shl(96, token)))
mstore(add(m, 0x54), amount)
mstore(add(m, 0x34), to)
mstore(add(m, 0x20), shl(96, from))
// `transferFrom(address,address,uint160,address)`.
mstore(m, 0x36c78516000000000000000000000000)
let p := PERMIT2
let exists := eq(chainid(), 1)
if iszero(exists) { exists := iszero(iszero(extcodesize(p))) }
if iszero(
and(
call(gas(), p, 0, add(m, 0x10), 0x84, codesize(), 0x00),
lt(iszero(extcodesize(token)), exists) // Token has code and Permit2 exists.
)
) {
mstore(0x00, 0x7939f4248757f0fd) // `TransferFromFailed()` or `Permit2AmountOverflow()`.
revert(add(0x18, shl(2, iszero(iszero(shr(160, amount))))), 0x04)
}
}
}
/// @dev Permit a user to spend a given amount of
/// another user's tokens via native EIP-2612 permit if possible, falling
/// back to Permit2 if native permit fails or is not implemented on the token.
function permit2(
address token,
address owner,
address spender,
uint256 amount,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) internal {
bool success;
/// @solidity memory-safe-assembly
assembly {
for {} shl(96, xor(token, WETH9)) {} {
mstore(0x00, 0x3644e515) // `DOMAIN_SEPARATOR()`.
if iszero(
and( // The arguments of `and` are evaluated from right to left.
lt(iszero(mload(0x00)), eq(returndatasize(), 0x20)), // Returns 1 non-zero word.
// Gas stipend to limit gas burn for tokens that don't refund gas when
// an non-existing function is called. 5K should be enough for a SLOAD.
staticcall(5000, token, 0x1c, 0x04, 0x00, 0x20)
)
) { break }
// After here, we can be sure that token is a contract.
let m := mload(0x40)
mstore(add(m, 0x34), spender)
mstore(add(m, 0x20), shl(96, owner))
mstore(add(m, 0x74), deadline)
if eq(mload(0x00), DAI_DOMAIN_SEPARATOR) {
mstore(0x14, owner)
mstore(0x00, 0x7ecebe00000000000000000000000000) // `nonces(address)`.
mstore(
add(m, 0x94),
lt(iszero(amount), staticcall(gas(), token, 0x10, 0x24, add(m, 0x54), 0x20))
)
mstore(m, 0x8fcbaf0c000000000000000000000000) // `IDAIPermit.permit`.
// `nonces` is already at `add(m, 0x54)`.
// `amount != 0` is already stored at `add(m, 0x94)`.
mstore(add(m, 0xb4), and(0xff, v))
mstore(add(m, 0xd4), r)
mstore(add(m, 0xf4), s)
success := call(gas(), token, 0, add(m, 0x10), 0x104, codesize(), 0x00)
break
}
mstore(m, 0xd505accf000000000000000000000000) // `IERC20Permit.permit`.
mstore(add(m, 0x54), amount)
mstore(add(m, 0x94), and(0xff, v))
mstore(add(m, 0xb4), r)
mstore(add(m, 0xd4), s)
success := call(gas(), token, 0, add(m, 0x10), 0xe4, codesize(), 0x00)
break
}
}
if (!success) simplePermit2(token, owner, spender, amount, deadline, v, r, s);
}
/// @dev Simple permit on the Permit2 contract.
function simplePermit2(
address token,
address owner,
address spender,
uint256 amount,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) internal {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(m, 0x927da105) // `allowance(address,address,address)`.
{
let addressMask := shr(96, not(0))
mstore(add(m, 0x20), and(addressMask, owner))
mstore(add(m, 0x40), and(addressMask, token))
mstore(add(m, 0x60), and(addressMask, spender))
mstore(add(m, 0xc0), and(addressMask, spender))
}
let p := mul(PERMIT2, iszero(shr(160, amount)))
if iszero(
and( // The arguments of `and` are evaluated from right to left.
gt(returndatasize(), 0x5f), // Returns 3 words: `amount`, `expiration`, `nonce`.
staticcall(gas(), p, add(m, 0x1c), 0x64, add(m, 0x60), 0x60)
)
) {
mstore(0x00, 0x6b836e6b8757f0fd) // `Permit2Failed()` or `Permit2AmountOverflow()`.
revert(add(0x18, shl(2, iszero(p))), 0x04)
}
mstore(m, 0x2b67b570) // `Permit2.permit` (PermitSingle variant).
// `owner` is already `add(m, 0x20)`.
// `token` is already at `add(m, 0x40)`.
mstore(add(m, 0x60), amount)
mstore(add(m, 0x80), 0xffffffffffff) // `expiration = type(uint48).max`.
// `nonce` is already at `add(m, 0xa0)`.
// `spender` is already at `add(m, 0xc0)`.
mstore(add(m, 0xe0), deadline)
mstore(add(m, 0x100), 0x100) // `signature` offset.
mstore(add(m, 0x120), 0x41) // `signature` length.
mstore(add(m, 0x140), r)
mstore(add(m, 0x160), s)
mstore(add(m, 0x180), shl(248, v))
if iszero( // Revert if token does not have code, or if the call fails.
mul(extcodesize(token), call(gas(), p, 0, add(m, 0x1c), 0x184, codesize(), 0x00))) {
mstore(0x00, 0x6b836e6b) // `Permit2Failed()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Approves `spender` to spend `amount` of `token` for `address(this)`.
function permit2Approve(address token, address spender, uint160 amount, uint48 expiration)
internal
{
/// @solidity memory-safe-assembly
assembly {
let addressMask := shr(96, not(0))
let m := mload(0x40)
mstore(m, 0x87517c45) // `approve(address,address,uint160,uint48)`.
mstore(add(m, 0x20), and(addressMask, token))
mstore(add(m, 0x40), and(addressMask, spender))
mstore(add(m, 0x60), and(addressMask, amount))
mstore(add(m, 0x80), and(0xffffffffffff, expiration))
if iszero(call(gas(), PERMIT2, 0, add(m, 0x1c), 0xa0, codesize(), 0x00)) {
mstore(0x00, 0x324f14ae) // `Permit2ApproveFailed()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Revokes an approval for `token` and `spender` for `address(this)`.
function permit2Lockdown(address token, address spender) internal {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(m, 0xcc53287f) // `Permit2.lockdown`.
mstore(add(m, 0x20), 0x20) // Offset of the `approvals`.
mstore(add(m, 0x40), 1) // `approvals.length`.
mstore(add(m, 0x60), shr(96, shl(96, token)))
mstore(add(m, 0x80), shr(96, shl(96, spender)))
if iszero(call(gas(), PERMIT2, 0, add(m, 0x1c), 0xa0, codesize(), 0x00)) {
mstore(0x00, 0x96b3de23) // `Permit2LockdownFailed()`.
revert(0x1c, 0x04)
}
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Safe integer casting library that reverts on overflow.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SafeCastLib.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/math/SafeCast.sol)
/// @dev Optimized for runtime gas for very high number of optimizer runs (i.e. >= 1000000).
library SafeCastLib {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Unable to cast to the target type due to overflow.
error Overflow();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* UNSIGNED INTEGER SAFE CASTING OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Casts `x` to a uint8. Reverts on overflow.
function toUint8(uint256 x) internal pure returns (uint8) {
if (x >= 1 << 8) _revertOverflow();
return uint8(x);
}
/// @dev Casts `x` to a uint16. Reverts on overflow.
function toUint16(uint256 x) internal pure returns (uint16) {
if (x >= 1 << 16) _revertOverflow();
return uint16(x);
}
/// @dev Casts `x` to a uint24. Reverts on overflow.
function toUint24(uint256 x) internal pure returns (uint24) {
if (x >= 1 << 24) _revertOverflow();
return uint24(x);
}
/// @dev Casts `x` to a uint32. Reverts on overflow.
function toUint32(uint256 x) internal pure returns (uint32) {
if (x >= 1 << 32) _revertOverflow();
return uint32(x);
}
/// @dev Casts `x` to a uint40. Reverts on overflow.
function toUint40(uint256 x) internal pure returns (uint40) {
if (x >= 1 << 40) _revertOverflow();
return uint40(x);
}
/// @dev Casts `x` to a uint48. Reverts on overflow.
function toUint48(uint256 x) internal pure returns (uint48) {
if (x >= 1 << 48) _revertOverflow();
return uint48(x);
}
/// @dev Casts `x` to a uint56. Reverts on overflow.
function toUint56(uint256 x) internal pure returns (uint56) {
if (x >= 1 << 56) _revertOverflow();
return uint56(x);
}
/// @dev Casts `x` to a uint64. Reverts on overflow.
function toUint64(uint256 x) internal pure returns (uint64) {
if (x >= 1 << 64) _revertOverflow();
return uint64(x);
}
/// @dev Casts `x` to a uint72. Reverts on overflow.
function toUint72(uint256 x) internal pure returns (uint72) {
if (x >= 1 << 72) _revertOverflow();
return uint72(x);
}
/// @dev Casts `x` to a uint80. Reverts on overflow.
function toUint80(uint256 x) internal pure returns (uint80) {
if (x >= 1 << 80) _revertOverflow();
return uint80(x);
}
/// @dev Casts `x` to a uint88. Reverts on overflow.
function toUint88(uint256 x) internal pure returns (uint88) {
if (x >= 1 << 88) _revertOverflow();
return uint88(x);
}
/// @dev Casts `x` to a uint96. Reverts on overflow.
function toUint96(uint256 x) internal pure returns (uint96) {
if (x >= 1 << 96) _revertOverflow();
return uint96(x);
}
/// @dev Casts `x` to a uint104. Reverts on overflow.
function toUint104(uint256 x) internal pure returns (uint104) {
if (x >= 1 << 104) _revertOverflow();
return uint104(x);
}
/// @dev Casts `x` to a uint112. Reverts on overflow.
function toUint112(uint256 x) internal pure returns (uint112) {
if (x >= 1 << 112) _revertOverflow();
return uint112(x);
}
/// @dev Casts `x` to a uint120. Reverts on overflow.
function toUint120(uint256 x) internal pure returns (uint120) {
if (x >= 1 << 120) _revertOverflow();
return uint120(x);
}
/// @dev Casts `x` to a uint128. Reverts on overflow.
function toUint128(uint256 x) internal pure returns (uint128) {
if (x >= 1 << 128) _revertOverflow();
return uint128(x);
}
/// @dev Casts `x` to a uint136. Reverts on overflow.
function toUint136(uint256 x) internal pure returns (uint136) {
if (x >= 1 << 136) _revertOverflow();
return uint136(x);
}
/// @dev Casts `x` to a uint144. Reverts on overflow.
function toUint144(uint256 x) internal pure returns (uint144) {
if (x >= 1 << 144) _revertOverflow();
return uint144(x);
}
/// @dev Casts `x` to a uint152. Reverts on overflow.
function toUint152(uint256 x) internal pure returns (uint152) {
if (x >= 1 << 152) _revertOverflow();
return uint152(x);
}
/// @dev Casts `x` to a uint160. Reverts on overflow.
function toUint160(uint256 x) internal pure returns (uint160) {
if (x >= 1 << 160) _revertOverflow();
return uint160(x);
}
/// @dev Casts `x` to a uint168. Reverts on overflow.
function toUint168(uint256 x) internal pure returns (uint168) {
if (x >= 1 << 168) _revertOverflow();
return uint168(x);
}
/// @dev Casts `x` to a uint176. Reverts on overflow.
function toUint176(uint256 x) internal pure returns (uint176) {
if (x >= 1 << 176) _revertOverflow();
return uint176(x);
}
/// @dev Casts `x` to a uint184. Reverts on overflow.
function toUint184(uint256 x) internal pure returns (uint184) {
if (x >= 1 << 184) _revertOverflow();
return uint184(x);
}
/// @dev Casts `x` to a uint192. Reverts on overflow.
function toUint192(uint256 x) internal pure returns (uint192) {
if (x >= 1 << 192) _revertOverflow();
return uint192(x);
}
/// @dev Casts `x` to a uint200. Reverts on overflow.
function toUint200(uint256 x) internal pure returns (uint200) {
if (x >= 1 << 200) _revertOverflow();
return uint200(x);
}
/// @dev Casts `x` to a uint208. Reverts on overflow.
function toUint208(uint256 x) internal pure returns (uint208) {
if (x >= 1 << 208) _revertOverflow();
return uint208(x);
}
/// @dev Casts `x` to a uint216. Reverts on overflow.
function toUint216(uint256 x) internal pure returns (uint216) {
if (x >= 1 << 216) _revertOverflow();
return uint216(x);
}
/// @dev Casts `x` to a uint224. Reverts on overflow.
function toUint224(uint256 x) internal pure returns (uint224) {
if (x >= 1 << 224) _revertOverflow();
return uint224(x);
}
/// @dev Casts `x` to a uint232. Reverts on overflow.
function toUint232(uint256 x) internal pure returns (uint232) {
if (x >= 1 << 232) _revertOverflow();
return uint232(x);
}
/// @dev Casts `x` to a uint240. Reverts on overflow.
function toUint240(uint256 x) internal pure returns (uint240) {
if (x >= 1 << 240) _revertOverflow();
return uint240(x);
}
/// @dev Casts `x` to a uint248. Reverts on overflow.
function toUint248(uint256 x) internal pure returns (uint248) {
if (x >= 1 << 248) _revertOverflow();
return uint248(x);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* SIGNED INTEGER SAFE CASTING OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Casts `x` to a int8. Reverts on overflow.
function toInt8(int256 x) internal pure returns (int8) {
unchecked {
if (((1 << 7) + uint256(x)) >> 8 == uint256(0)) return int8(x);
_revertOverflow();
}
}
/// @dev Casts `x` to a int16. Reverts on overflow.
function toInt16(int256 x) internal pure returns (int16) {
unchecked {
if (((1 << 15) + uint256(x)) >> 16 == uint256(0)) return int16(x);
_revertOverflow();
}
}
/// @dev Casts `x` to a int24. Reverts on overflow.
function toInt24(int256 x) internal pure returns (int24) {
unchecked {
if (((1 << 23) + uint256(x)) >> 24 == uint256(0)) return int24(x);
_revertOverflow();
}
}
/// @dev Casts `x` to a int32. Reverts on overflow.
function toInt32(int256 x) internal pure returns (int32) {
unchecked {
if (((1 << 31) + uint256(x)) >> 32 == uint256(0)) return int32(x);
_revertOverflow();
}
}
/// @dev Casts `x` to a int40. Reverts on overflow.
function toInt40(int256 x) internal pure returns (int40) {
unchecked {
if (((1 << 39) + uint256(x)) >> 40 == uint256(0)) return int40(x);
_revertOverflow();
}
}
/// @dev Casts `x` to a int48. Reverts on overflow.
function toInt48(int256 x) internal pure returns (int48) {
unchecked {
if (((1 << 47) + uint256(x)) >> 48 == uint256(0)) return int48(x);
_revertOverflow();
}
}
/// @dev Casts `x` to a int56. Reverts on overflow.
function toInt56(int256 x) internal pure returns (int56) {
unchecked {
if (((1 << 55) + uint256(x)) >> 56 == uint256(0)) return int56(x);
_revertOverflow();
}
}
/// @dev Casts `x` to a int64. Reverts on overflow.
function toInt64(int256 x) internal pure returns (int64) {
unchecked {
if (((1 << 63) + uint256(x)) >> 64 == uint256(0)) return int64(x);
_revertOverflow();
}
}
/// @dev Casts `x` to a int72. Reverts on overflow.
function toInt72(int256 x) internal pure returns (int72) {
unchecked {
if (((1 << 71) + uint256(x)) >> 72 == uint256(0)) return int72(x);
_revertOverflow();
}
}
/// @dev Casts `x` to a int80. Reverts on overflow.
function toInt80(int256 x) internal pure returns (int80) {
unchecked {
if (((1 << 79) + uint256(x)) >> 80 == uint256(0)) return int80(x);
_revertOverflow();
}
}
/// @dev Casts `x` to a int88. Reverts on overflow.
function toInt88(int256 x) internal pure returns (int88) {
unchecked {
if (((1 << 87) + uint256(x)) >> 88 == uint256(0)) return int88(x);
_revertOverflow();
}
}
/// @dev Casts `x` to a int96. Reverts on overflow.
function toInt96(int256 x) internal pure returns (int96) {
unchecked {
if (((1 << 95) + uint256(x)) >> 96 == uint256(0)) return int96(x);
_revertOverflow();
}
}
/// @dev Casts `x` to a int104. Reverts on overflow.
function toInt104(int256 x) internal pure returns (int104) {
unchecked {
if (((1 << 103) + uint256(x)) >> 104 == uint256(0)) return int104(x);
_revertOverflow();
}
}
/// @dev Casts `x` to a int112. Reverts on overflow.
function toInt112(int256 x) internal pure returns (int112) {
unchecked {
if (((1 << 111) + uint256(x)) >> 112 == uint256(0)) return int112(x);
_revertOverflow();
}
}
/// @dev Casts `x` to a int120. Reverts on overflow.
function toInt120(int256 x) internal pure returns (int120) {
unchecked {
if (((1 << 119) + uint256(x)) >> 120 == uint256(0)) return int120(x);
_revertOverflow();
}
}
/// @dev Casts `x` to a int128. Reverts on overflow.
function toInt128(int256 x) internal pure returns (int128) {
unchecked {
if (((1 << 127) + uint256(x)) >> 128 == uint256(0)) return int128(x);
_revertOverflow();
}
}
/// @dev Casts `x` to a int136. Reverts on overflow.
function toInt136(int256 x) internal pure returns (int136) {
unchecked {
if (((1 << 135) + uint256(x)) >> 136 == uint256(0)) return int136(x);
_revertOverflow();
}
}
/// @dev Casts `x` to a int144. Reverts on overflow.
function toInt144(int256 x) internal pure returns (int144) {
unchecked {
if (((1 << 143) + uint256(x)) >> 144 == uint256(0)) return int144(x);
_revertOverflow();
}
}
/// @dev Casts `x` to a int152. Reverts on overflow.
function toInt152(int256 x) internal pure returns (int152) {
unchecked {
if (((1 << 151) + uint256(x)) >> 152 == uint256(0)) return int152(x);
_revertOverflow();
}
}
/// @dev Casts `x` to a int160. Reverts on overflow.
function toInt160(int256 x) internal pure returns (int160) {
unchecked {
if (((1 << 159) + uint256(x)) >> 160 == uint256(0)) return int160(x);
_revertOverflow();
}
}
/// @dev Casts `x` to a int168. Reverts on overflow.
function toInt168(int256 x) internal pure returns (int168) {
unchecked {
if (((1 << 167) + uint256(x)) >> 168 == uint256(0)) return int168(x);
_revertOverflow();
}
}
/// @dev Casts `x` to a int176. Reverts on overflow.
function toInt176(int256 x) internal pure returns (int176) {
unchecked {
if (((1 << 175) + uint256(x)) >> 176 == uint256(0)) return int176(x);
_revertOverflow();
}
}
/// @dev Casts `x` to a int184. Reverts on overflow.
function toInt184(int256 x) internal pure returns (int184) {
unchecked {
if (((1 << 183) + uint256(x)) >> 184 == uint256(0)) return int184(x);
_revertOverflow();
}
}
/// @dev Casts `x` to a int192. Reverts on overflow.
function toInt192(int256 x) internal pure returns (int192) {
unchecked {
if (((1 << 191) + uint256(x)) >> 192 == uint256(0)) return int192(x);
_revertOverflow();
}
}
/// @dev Casts `x` to a int200. Reverts on overflow.
function toInt200(int256 x) internal pure returns (int200) {
unchecked {
if (((1 << 199) + uint256(x)) >> 200 == uint256(0)) return int200(x);
_revertOverflow();
}
}
/// @dev Casts `x` to a int208. Reverts on overflow.
function toInt208(int256 x) internal pure returns (int208) {
unchecked {
if (((1 << 207) + uint256(x)) >> 208 == uint256(0)) return int208(x);
_revertOverflow();
}
}
/// @dev Casts `x` to a int216. Reverts on overflow.
function toInt216(int256 x) internal pure returns (int216) {
unchecked {
if (((1 << 215) + uint256(x)) >> 216 == uint256(0)) return int216(x);
_revertOverflow();
}
}
/// @dev Casts `x` to a int224. Reverts on overflow.
function toInt224(int256 x) internal pure returns (int224) {
unchecked {
if (((1 << 223) + uint256(x)) >> 224 == uint256(0)) return int224(x);
_revertOverflow();
}
}
/// @dev Casts `x` to a int232. Reverts on overflow.
function toInt232(int256 x) internal pure returns (int232) {
unchecked {
if (((1 << 231) + uint256(x)) >> 232 == uint256(0)) return int232(x);
_revertOverflow();
}
}
/// @dev Casts `x` to a int240. Reverts on overflow.
function toInt240(int256 x) internal pure returns (int240) {
unchecked {
if (((1 << 239) + uint256(x)) >> 240 == uint256(0)) return int240(x);
_revertOverflow();
}
}
/// @dev Casts `x` to a int248. Reverts on overflow.
function toInt248(int256 x) internal pure returns (int248) {
unchecked {
if (((1 << 247) + uint256(x)) >> 248 == uint256(0)) return int248(x);
_revertOverflow();
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* OTHER SAFE CASTING OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Casts `x` to a int8. Reverts on overflow.
function toInt8(uint256 x) internal pure returns (int8) {
if (x >= 1 << 7) _revertOverflow();
return int8(int256(x));
}
/// @dev Casts `x` to a int16. Reverts on overflow.
function toInt16(uint256 x) internal pure returns (int16) {
if (x >= 1 << 15) _revertOverflow();
return int16(int256(x));
}
/// @dev Casts `x` to a int24. Reverts on overflow.
function toInt24(uint256 x) internal pure returns (int24) {
if (x >= 1 << 23) _revertOverflow();
return int24(int256(x));
}
/// @dev Casts `x` to a int32. Reverts on overflow.
function toInt32(uint256 x) internal pure returns (int32) {
if (x >= 1 << 31) _revertOverflow();
return int32(int256(x));
}
/// @dev Casts `x` to a int40. Reverts on overflow.
function toInt40(uint256 x) internal pure returns (int40) {
if (x >= 1 << 39) _revertOverflow();
return int40(int256(x));
}
/// @dev Casts `x` to a int48. Reverts on overflow.
function toInt48(uint256 x) internal pure returns (int48) {
if (x >= 1 << 47) _revertOverflow();
return int48(int256(x));
}
/// @dev Casts `x` to a int56. Reverts on overflow.
function toInt56(uint256 x) internal pure returns (int56) {
if (x >= 1 << 55) _revertOverflow();
return int56(int256(x));
}
/// @dev Casts `x` to a int64. Reverts on overflow.
function toInt64(uint256 x) internal pure returns (int64) {
if (x >= 1 << 63) _revertOverflow();
return int64(int256(x));
}
/// @dev Casts `x` to a int72. Reverts on overflow.
function toInt72(uint256 x) internal pure returns (int72) {
if (x >= 1 << 71) _revertOverflow();
return int72(int256(x));
}
/// @dev Casts `x` to a int80. Reverts on overflow.
function toInt80(uint256 x) internal pure returns (int80) {
if (x >= 1 << 79) _revertOverflow();
return int80(int256(x));
}
/// @dev Casts `x` to a int88. Reverts on overflow.
function toInt88(uint256 x) internal pure returns (int88) {
if (x >= 1 << 87) _revertOverflow();
return int88(int256(x));
}
/// @dev Casts `x` to a int96. Reverts on overflow.
function toInt96(uint256 x) internal pure returns (int96) {
if (x >= 1 << 95) _revertOverflow();
return int96(int256(x));
}
/// @dev Casts `x` to a int104. Reverts on overflow.
function toInt104(uint256 x) internal pure returns (int104) {
if (x >= 1 << 103) _revertOverflow();
return int104(int256(x));
}
/// @dev Casts `x` to a int112. Reverts on overflow.
function toInt112(uint256 x) internal pure returns (int112) {
if (x >= 1 << 111) _revertOverflow();
return int112(int256(x));
}
/// @dev Casts `x` to a int120. Reverts on overflow.
function toInt120(uint256 x) internal pure returns (int120) {
if (x >= 1 << 119) _revertOverflow();
return int120(int256(x));
}
/// @dev Casts `x` to a int128. Reverts on overflow.
function toInt128(uint256 x) internal pure returns (int128) {
if (x >= 1 << 127) _revertOverflow();
return int128(int256(x));
}
/// @dev Casts `x` to a int136. Reverts on overflow.
function toInt136(uint256 x) internal pure returns (int136) {
if (x >= 1 << 135) _revertOverflow();
return int136(int256(x));
}
/// @dev Casts `x` to a int144. Reverts on overflow.
function toInt144(uint256 x) internal pure returns (int144) {
if (x >= 1 << 143) _revertOverflow();
return int144(int256(x));
}
/// @dev Casts `x` to a int152. Reverts on overflow.
function toInt152(uint256 x) internal pure returns (int152) {
if (x >= 1 << 151) _revertOverflow();
return int152(int256(x));
}
/// @dev Casts `x` to a int160. Reverts on overflow.
function toInt160(uint256 x) internal pure returns (int160) {
if (x >= 1 << 159) _revertOverflow();
return int160(int256(x));
}
/// @dev Casts `x` to a int168. Reverts on overflow.
function toInt168(uint256 x) internal pure returns (int168) {
if (x >= 1 << 167) _revertOverflow();
return int168(int256(x));
}
/// @dev Casts `x` to a int176. Reverts on overflow.
function toInt176(uint256 x) internal pure returns (int176) {
if (x >= 1 << 175) _revertOverflow();
return int176(int256(x));
}
/// @dev Casts `x` to a int184. Reverts on overflow.
function toInt184(uint256 x) internal pure returns (int184) {
if (x >= 1 << 183) _revertOverflow();
return int184(int256(x));
}
/// @dev Casts `x` to a int192. Reverts on overflow.
function toInt192(uint256 x) internal pure returns (int192) {
if (x >= 1 << 191) _revertOverflow();
return int192(int256(x));
}
/// @dev Casts `x` to a int200. Reverts on overflow.
function toInt200(uint256 x) internal pure returns (int200) {
if (x >= 1 << 199) _revertOverflow();
return int200(int256(x));
}
/// @dev Casts `x` to a int208. Reverts on overflow.
function toInt208(uint256 x) internal pure returns (int208) {
if (x >= 1 << 207) _revertOverflow();
return int208(int256(x));
}
/// @dev Casts `x` to a int216. Reverts on overflow.
function toInt216(uint256 x) internal pure returns (int216) {
if (x >= 1 << 215) _revertOverflow();
return int216(int256(x));
}
/// @dev Casts `x` to a int224. Reverts on overflow.
function toInt224(uint256 x) internal pure returns (int224) {
if (x >= 1 << 223) _revertOverflow();
return int224(int256(x));
}
/// @dev Casts `x` to a int232. Reverts on overflow.
function toInt232(uint256 x) internal pure returns (int232) {
if (x >= 1 << 231) _revertOverflow();
return int232(int256(x));
}
/// @dev Casts `x` to a int240. Reverts on overflow.
function toInt240(uint256 x) internal pure returns (int240) {
if (x >= 1 << 239) _revertOverflow();
return int240(int256(x));
}
/// @dev Casts `x` to a int248. Reverts on overflow.
function toInt248(uint256 x) internal pure returns (int248) {
if (x >= 1 << 247) _revertOverflow();
return int248(int256(x));
}
/// @dev Casts `x` to a int256. Reverts on overflow.
function toInt256(uint256 x) internal pure returns (int256) {
if (int256(x) >= 0) return int256(x);
_revertOverflow();
}
/// @dev Casts `x` to a uint256. Reverts on overflow.
function toUint256(int256 x) internal pure returns (uint256) {
if (x >= 0) return uint256(x);
_revertOverflow();
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* PRIVATE HELPERS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
function _revertOverflow() private pure {
/// @solidity memory-safe-assembly
assembly {
// Store the function selector of `Overflow()`.
mstore(0x00, 0x35278d12)
// Revert with (offset, size).
revert(0x1c, 0x04)
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
pragma solidity ^0.8.20;
import {Context} from "../utils/Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* The initial owner is set to the address provided by the deployer. This can
* later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract Ownable is Context {
address private _owner;
/**
* @dev The caller account is not authorized to perform an operation.
*/
error OwnableUnauthorizedAccount(address account);
/**
* @dev The owner is not a valid owner account. (eg. `address(0)`)
*/
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the address provided by the deployer as the initial owner.
*/
constructor(address initialOwner) {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}//SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.8.28 <0.9.0;
import { IERC4626Custom } from "./IERC4626Custom.sol";
import { IERC20Full } from "./IERC20Full.sol";
import { IMonadStaking } from "./IMonadStaking.sol";
import { Policy, UncommitApproval, ValidatorStats } from "../Types.sol";
/**
* @title IShMonad - Interface for the ShMonad Liquid Staking Token contract
* @notice Canonical NatSpec lives on the concrete mixins; this interface captures every external/public entrypoint.
*/
interface IShMonad is IERC4626Custom, IERC20Full {
// --------------------------------------------- //
// Initialization & Supply Views //
// --------------------------------------------- //
function initialize(address deployer) external;
function realTotalSupply() external view returns (uint256);
function committedTotalSupply() external view returns (uint256);
// --------------------------------------------- //
// Yield & Detailed ERC4626 Previews //
// --------------------------------------------- //
function boostYield(address yieldOriginator) external payable;
function boostYield(uint256 shares, address from, address yieldOriginator) external;
function sendValidatorRewards(uint64 validatorId, uint256 feeRate) external payable;
function previewRedeemDetailed(uint256 shares)
external
view
returns (uint256 grossAssets, uint256 feeAssets, uint256 netAssets);
function previewWithdrawDetailed(uint256 netAssets)
external
view
returns (uint256 shares, uint256 grossAssets, uint256 feeAssets);
function previewUnstake(uint256 shares) external view returns (uint256 assets);
// --------------------------------------------- //
// Account & Commitment Flow //
// --------------------------------------------- //
function commit(uint64 policyID, address commitRecipient, uint256 shares) external;
function depositAndCommit(
uint64 policyID,
address commitRecipient,
uint256 shMonToCommit
)
external
payable
returns (uint256 sharesMinted);
function requestUncommit(
uint64 policyID,
uint256 shares,
uint256 newMinBalance
)
external
returns (uint256 uncommitCompleteBlock);
function requestUncommitWithApprovedCompletor(
uint64 policyID,
uint256 shares,
uint256 newMinBalance,
address completor
)
external
returns (uint256 uncommitCompleteBlock);
function completeUncommit(uint64 policyID, uint256 shares) external;
function completeUncommitAndRedeem(uint64 policyID, uint256 shares) external returns (uint256 assets);
function completeUncommitAndRecommit(
uint64 fromPolicyID,
uint64 toPolicyID,
address commitRecipient,
uint256 shares
)
external;
function completeUncommitWithApproval(uint64 policyID, uint256 shares, address account) external;
function setUncommitApproval(uint64 policyID, address completor, uint256 shares) external;
// --------------------------------------------- //
// Unstake Lifecycle //
// --------------------------------------------- //
function requestUnstake(uint256 shares) external returns (uint64 completionEpoch);
function completeUnstake() external;
function getUnstakeRequest(address account) external view returns (uint128 amountMon, uint64 completionEpoch);
// --------------------------------------------- //
// Agent & Hold Operations //
// --------------------------------------------- //
function hold(uint64 policyID, address account, uint256 shares) external;
function release(uint64 policyID, address account, uint256 shares) external;
function batchHold(uint64 policyID, address[] calldata accounts, uint256[] memory amounts) external;
function batchRelease(uint64 policyID, address[] calldata accounts, uint256[] calldata amounts) external;
function agentTransferFromCommitted(
uint64 policyID,
address from,
address to,
uint256 amount,
uint256 fromReleaseAmount,
bool inUnderlying
)
external;
function agentTransferToUncommitted(
uint64 policyID,
address from,
address to,
uint256 amount,
uint256 fromReleaseAmount,
bool inUnderlying
)
external;
function agentWithdrawFromCommitted(
uint64 policyID,
address from,
address to,
uint256 amount,
uint256 fromReleaseAmount,
bool amountSpecifiedInUnderlying
)
external;
function getHoldAmount(uint64 policyID, address account) external view returns (uint256);
// --------------------------------------------- //
// Top-Up Settings & Balance Views //
// --------------------------------------------- //
function setMinCommittedBalance(
uint64 policyID,
uint128 minCommitted,
uint128 maxTopUpPerPeriod,
uint32 topUpPeriodDuration
)
external;
function getTopUpSettings(
uint64 policyID,
address account
)
external
view
returns (uint128 maxTopUpPerPeriod, uint32 topUpPeriodDuration);
function getCommittedData(
uint64 policyID,
address account
)
external
view
returns (uint128 committed, uint128 minCommitted);
function getUncommittingData(
uint64 policyID,
address account
)
external
view
returns (uint128 uncommitting, uint48 uncommitStartBlock);
function policyBalanceAvailable(
uint64 policyID,
address account,
bool inUnderlying
)
external
view
returns (uint256 balanceAvailable);
function topUpAvailable(
uint64 policyID,
address account,
bool inUnderlying
)
external
view
returns (uint256 amountAvailable);
function getUncommitApproval(
uint64 policyID,
address account
)
external
view
returns (UncommitApproval memory approval);
function uncommittingCompleteBlock(uint64 policyID, address account) external view returns (uint256);
function balanceOfCommitted(address account) external view returns (uint256);
function balanceOfCommitted(uint64 policyID, address account) external view returns (uint256);
function balanceOfUncommitting(uint64 policyID, address account) external view returns (uint256);
// --------------------------------------------- //
// Policy Management //
// --------------------------------------------- //
function createPolicy(uint48 escrowDuration) external returns (uint64 policyID);
function addPolicyAgent(uint64 policyID, address agent) external;
function removePolicyAgent(uint64 policyID, address agent) external;
function disablePolicy(uint64 policyID) external;
function policyCount() external view returns (uint64);
function getPolicy(uint64 policyID) external view returns (Policy memory);
function isPolicyAgent(uint64 policyID, address agent) external view returns (bool);
function getPolicyAgents(uint64 policyID) external view returns (address[] memory);
// --------------------------------------------- //
// Atomic Unstake Pool Management //
// --------------------------------------------- //
function setPoolTargetLiquidityPercentage(uint256 newPercentageScaled) external;
function setUnstakeFeeCurve(uint256 newSlopeRateRay, uint256 newYInterceptRay) external;
function yInterceptRay() external view returns (uint256);
function slopeRateRay() external view returns (uint256);
function getCurrentLiquidity() external view returns (uint256);
function getTargetLiquidity() external view returns (uint256);
function getPendingTargetLiquidity() external view returns (uint256);
function getAtomicPoolUtilization()
external
view
returns (uint256 utilized, uint256 allocated, uint256 available, uint256 utilizationWad);
function getFeeCurveParams() external view returns (uint256 slopeRateRayOut, uint256 yInterceptRayOut);
function getAtomicUtilizationWad() external view returns (uint256 utilizationWad);
function getCurrentUnstakeFeeRateRay() external view returns (uint256 feeRateRay);
// --------------------------------------------- //
// Global Accounting & Cranking //
// --------------------------------------------- //
function crank() external returns (bool complete);
function isGlobalCrankAvailable() external returns (bool);
function isValidatorCrankAvailable(uint64 validatorId) external view returns (bool);
function getWorkingCapital() external view returns (uint128 stakedAmount, uint128 reservedAmount);
function getAtomicCapital() external view returns (uint128 allocatedAmount, uint128 distributedAmount);
function getGlobalPending() external view returns (uint120 pendingStaking, uint120 pendingUnstaking);
function getGlobalCashFlows(int256 epochPointer)
external
view
returns (uint120 queueToStake, uint120 queueForUnstake);
function getGlobalRevenue(int256 epochPointer)
external
view
returns (uint120 rewardsPayable, uint120 earnedRevenue);
function getGlobalEpoch(int256 epochPointer)
external
view
returns (
uint64 epoch,
uint8 withdrawalId,
bool hasWithdrawal,
bool hasDeposit,
bool crankedInBoundaryPeriod,
bool wasCranked,
bool frozen,
bool closed,
uint128 targetStakeAmount
);
function getInternalEpoch() external view returns (uint64);
function getGlobalStatus(int256 epochPointer) external view returns (bool frozen, bool closed);
function getScaledTargetLiquidityPercentage() external view returns (uint256);
function getGlobalAmountAvailableToUnstake() external view returns (uint256 amount);
function getCurrentAssets() external view returns (uint256);
function globalLiabilities()
external
view
returns (uint128 rewardsPayable, uint128 redemptionsPayable, uint128 commissionPayable);
function getAdminValues()
external
view
returns (
uint64 internalEpoch,
uint16 targetLiquidityPercentage,
uint16 incentiveAlignmentPercentage,
uint16 stakingCommission,
uint16 boostCommissionRate,
uint128 commissionPayable
);
function STAKING_PRECOMPILE() external pure returns (IMonadStaking);
// --------------------------------------------- //
// Validator Administration //
// --------------------------------------------- //
function deactivateValidator(uint64 validatorId) external;
function addValidator(uint64 validatorId, address coinbase) external;
function addValidator(uint64 validatorId) external returns (address coinbase);
function updateStakingCommission(uint16 feeInBps) external;
function updateBoostCommission(uint16 feeInBps) external;
function updateIncentiveAlignmentPercentage(uint16 percentageInBps) external;
function setFrozenStatus(bool isFrozen) external;
function setClosedStatus(bool isClosed) external;
function claimOwnerCommissionAsShares(uint256 assets, address receiver) external returns (uint256 sharesMinted);
// --------------------------------------------- //
// Validator Views //
// --------------------------------------------- //
function previewCoinbaseAddress(uint64 validatorId) external view returns (address predicted);
function getValidatorStats(uint64 validatorId) external view returns (ValidatorStats memory stats);
function isValidatorActive(uint64 validatorId) external view returns (bool);
function getEpochInfo() external returns (uint256 epochNumber, uint256 epochStartBlock);
function getValidatorCoinbase(uint256 validatorId) external view returns (address);
function getValidatorIdForCoinbase(address coinbase) external view returns (uint256);
function getValidatorData(uint64 validatorId)
external
view
returns (
uint64 epoch,
uint64 id,
bool isPlaceholder,
bool isActive,
bool inActiveSet_Current,
bool inActiveSet_Last,
address coinbase
);
function listActiveValidators() external view returns (uint64[] memory validatorIds, address[] memory coinbases);
function getValidatorEpochs(uint64 validatorId)
external
view
returns (uint64 lastEpoch, uint128 lastTargetStakeAmount, uint64 currentEpoch, uint128 currentTargetStakeAmount);
function getValidatorPendingEscrow(uint64 validatorId)
external
view
returns (
uint120 lastPendingStaking,
uint120 lastPendingUnstaking,
uint120 currentPendingStaking,
uint120 currentPendingUnstaking
);
function getValidatorRewards(uint64 validatorId)
external
view
returns (
uint120 lastRewardsPayable,
uint120 lastEarnedRevenue,
uint120 currentRewardsPayable,
uint120 currentEarnedRevenue
);
function getValidatorNeighbors(uint64 validatorId) external view returns (address previous, address next);
function getActiveValidatorCount() external view returns (uint256);
function getNextValidatorToCrank() external view returns (address);
}//SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.8.28 <0.9.0;
struct ValidatorView {
address authAddress;
uint64 flags;
uint256 executionStake;
uint256 executionAccumulator;
uint256 executionCommission;
uint256 executionUnclaimedRewards;
uint256 consensusStake;
uint256 consensusCommission;
uint256 snapshotStake;
uint256 snapshotCommission;
bytes secpPubkey;
bytes blsPubkey;
}
struct DelInfo {
uint256 stake;
uint256 lastAccumulator;
uint256 rewards;
uint256 deltaStake;
uint256 nextDeltaStake;
uint64 deltaEpoch;
uint64 nextDeltaEpoch;
}
struct WithdrawalRequest {
uint256 amount;
uint256 accumulator;
uint64 epoch;
}
interface IMonadStaking {
function addValidator(
bytes calldata payload,
bytes calldata signedSecpMessage,
bytes calldata signedBlsMessage
)
external
payable
returns (uint64 validatorId);
function delegate(uint64 validatorId) external payable returns (bool success);
function undelegate(uint64 validatorId, uint256 amount, uint8 withdrawId) external returns (bool success);
function compound(uint64 validatorId) external returns (bool success);
function withdraw(uint64 validatorId, uint8 withdrawId) external returns (bool success);
function claimRewards(uint64 validatorId) external returns (bool success);
function changeCommission(uint64 validatorId, uint256 commission) external returns (bool success);
function externalReward(uint64 validatorId) external payable returns (bool success);
function getValidator(uint64 validatorId)
external
returns (
address authAddress,
uint64 flags,
uint256 stake,
uint256 accRewardPerToken,
uint256 commission,
uint256 unclaimedRewards,
uint256 consensusStake,
uint256 consensusCommission,
uint256 snapshotStake,
uint256 snapshotCommission,
bytes memory secpPubkey,
bytes memory blsPubkey
);
function getDelegator(
uint64 validatorId,
address delegator
)
external
returns (
uint256 stake,
uint256 accRewardPerToken,
uint256 unclaimedRewards,
uint256 deltaStake,
uint256 nextDeltaStake,
uint64 deltaEpoch,
uint64 nextDeltaEpoch
);
function getWithdrawalRequest(
uint64 validatorId,
address delegator,
uint8 withdrawId
)
external
returns (uint256 withdrawalAmount, uint256 accRewardPerToken, uint64 withdrawEpoch);
function getConsensusValidatorSet(uint32 startIndex)
external
returns (bool isDone, uint32 nextIndex, uint64[] memory valIds);
function getSnapshotValidatorSet(uint32 startIndex)
external
returns (bool isDone, uint32 nextIndex, uint64[] memory valIds);
function getExecutionValidatorSet(uint32 startIndex)
external
returns (bool isDone, uint32 nextIndex, uint64[] memory valIds);
function getDelegations(
address delegator,
uint64 startValId
)
external
returns (bool isDone, uint64 nextValId, uint64[] memory valIds);
function getDelegators(
uint64 validatorId,
address startDelegator
)
external
returns (bool isDone, address nextDelegator, address[] memory delegators);
function getEpoch() external returns (uint64 epoch, bool inEpochDelayPeriod);
/// @notice Returns the validator ID of the current block proposer/author for this block
/// @dev Temporary method name used by ShMonad to avoid relying on block.coinbase; mocked in tests
function getProposerValId() external returns (uint64 val_id);
function syscallOnEpochChange(uint64 epoch) external;
function syscallReward(address blockAuthor) external;
function syscallSnapshot() external;
// ================================ //
// Constants //
// ================================ //
// NOTE: The precompile has these constants internally but does NOT expose them as view functions.
// Production code uses DUST_THRESHOLD and WITHDRAWAL_DELAY from Constants.sol.
// Test code uses all constants from MockMonadStakingPrecompile.sol:
// - MON = 1e18
// - MIN_VALIDATE_STAKE = 100_000 * 1e18
// - ACTIVE_VALIDATOR_STAKE = 25_000_000 * 1e18
// - UNIT_BIAS = 1e36
// - DUST_THRESHOLD = 1e9 (used in production)
// - MAX_EXTERNAL_REWARD = 1e25
// - WITHDRAWAL_DELAY = 1 (used in production)
// - PAGINATED_RESULTS_SIZE = 100
event ValidatorCreated(uint64 indexed validatorId, address indexed authAddress);
event ValidatorStatusChanged(uint64 indexed validatorId, address indexed authAddress, uint64 flags);
event Delegate(uint64 indexed validatorId, address indexed delegator, uint256 amount, uint64 activationEpoch);
event Undelegate(
uint64 indexed validatorId, address indexed delegator, uint8 withdrawId, uint256 amount, uint64 activationEpoch
);
event Withdraw(
uint64 indexed validatorId, address indexed delegator, uint8 withdrawId, uint256 amount, uint64 withdrawEpoch
);
event ClaimRewards(uint256 indexed validatorId, address indexed delegator, uint256 amount);
event CommissionChanged(uint256 indexed validatorId, uint256 oldCommission, uint256 newCommission);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;
abstract contract FastLaneAuctionHandlerEvents {
event RelayWithdrawStuckERC20(address indexed receiver, address indexed token, uint256 amount);
event RelayWithdrawStuckNativeToken(address indexed receiver, uint256 amount);
event RelayFeeCollected(address indexed payor, uint64 indexed validatorId, uint256 amount);
event RelayBidFailed(address indexed searcher, bytes reason);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;
abstract contract FastLaneAuctionHandlerErrors {
error RelayPermissionSenderNotOrigin();
error RelayBidAmountIsZero();
error RelayNotRepaid(uint256 bidAmount, uint256 actualAmount);
error RelayCannotBeZero();
error RelayCannotBeSelf();
error RelayMustBeSelf();
error RelayNotActiveValidator();
error RelayProxyIsTimelocked();
error RelayInvalidSender();
error RelayProxyUpdateInvalid();
error RelayUnapprovedReentrancy();
error RelayShMonadDepositFailed();
error RelayTaskSubmissionFailed();
error RelayTargetBlockNumberExceeded();
error RelayTxHashesCountIsZero();
error RelayTxHashMustBeZero();
error RelayTxHashesCountInvalid();
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;
/// @notice Validator Data Struct
/// @dev Subject to BLOCK_TIMELOCK for changes
/// @param proxy Who to pay for this validator
/// @param timeUpdated Last time a change was requested for this validator proxy
/// @param blockOfLastWithdrawal Last time a withdrawal was initiated
struct ValidatorData {
address proxy;
uint64 blockOfLastWithdraw;
bool active;
uint256 timeUpdated;
}
enum BidType {
Null,
TopOfBlock,
Bundle,
Request,
Other
}
struct BidData {
uint128 amount;
uint64 blockNumber;
BidType bidType;
uint16 bidCount;
bool executable;
bool won;
}
interface ISearcherContract {
function fastLaneCall(address, uint256, bytes calldata) external payable returns (bool, bytes memory);
}// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.28; uint48 constant COMMIT_DURATION = 60; uint256 constant PROTOCOL_REVENUE_DENOMINATOR = 10_000; uint128 constant PROTOCOL_FEE_RATE = 1e17; // 10% shmonad protocol fee uint128 constant MEV_FEE_RATE = 1000; // 10% address constant PRECOMPILE_MONAD_STAKING = 0x0000000000000000000000000000000000001000;
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC20/IERC20.sol)
pragma solidity >=0.4.16;
/**
* @dev Interface of the ERC-20 standard as defined in the ERC.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 value) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 value) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC20/extensions/IERC20Metadata.sol)
pragma solidity >=0.6.2;
import {IERC20} from "../IERC20.sol";
/**
* @dev Interface for the optional metadata functions from the ERC-20 standard.
*/
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: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
/**
* @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;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/draft-IERC6093.sol)
pragma solidity >=0.8.4;
/**
* @dev Standard ERC-20 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-20 tokens.
*/
interface IERC20Errors {
/**
* @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param balance Current balance for the interacting account.
* @param needed Minimum amount required to perform a transfer.
*/
error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC20InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC20InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers.
* @param spender Address that may be allowed to operate on tokens without being their owner.
* @param allowance Amount of tokens a `spender` is allowed to operate with.
* @param needed Minimum amount required to perform a transfer.
*/
error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC20InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `spender` to be approved. Used in approvals.
* @param spender Address that may be allowed to operate on tokens without being their owner.
*/
error ERC20InvalidSpender(address spender);
}
/**
* @dev Standard ERC-721 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-721 tokens.
*/
interface IERC721Errors {
/**
* @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in ERC-20.
* Used in balance queries.
* @param owner Address of the current owner of a token.
*/
error ERC721InvalidOwner(address owner);
/**
* @dev Indicates a `tokenId` whose `owner` is the zero address.
* @param tokenId Identifier number of a token.
*/
error ERC721NonexistentToken(uint256 tokenId);
/**
* @dev Indicates an error related to the ownership over a particular token. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param tokenId Identifier number of a token.
* @param owner Address of the current owner of a token.
*/
error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC721InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC721InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `operator`’s approval. Used in transfers.
* @param operator Address that may be allowed to operate on tokens without being their owner.
* @param tokenId Identifier number of a token.
*/
error ERC721InsufficientApproval(address operator, uint256 tokenId);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC721InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `operator` to be approved. Used in approvals.
* @param operator Address that may be allowed to operate on tokens without being their owner.
*/
error ERC721InvalidOperator(address operator);
}
/**
* @dev Standard ERC-1155 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-1155 tokens.
*/
interface IERC1155Errors {
/**
* @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param balance Current balance for the interacting account.
* @param needed Minimum amount required to perform a transfer.
* @param tokenId Identifier number of a token.
*/
error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC1155InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC1155InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `operator`’s approval. Used in transfers.
* @param operator Address that may be allowed to operate on tokens without being their owner.
* @param owner Address of the current owner of a token.
*/
error ERC1155MissingApprovalForAll(address operator, address owner);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC1155InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `operator` to be approved. Used in approvals.
* @param operator Address that may be allowed to operate on tokens without being their owner.
*/
error ERC1155InvalidOperator(address operator);
/**
* @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation.
* Used in batch transfers.
* @param idsLength Length of the array of token identifiers
* @param valuesLength Length of the array of token amounts
*/
error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/**
* @dev Based on the interface of the ERC-4626 "Tokenized Vault Standard", as defined in
* https://eips.ethereum.org/EIPS/eip-4626[ERC-4626]. With some modifications.
*
* @dev Excludes IERC20 and IERC20Metadata interfaces, which are already included in IERC20Full.
*/
interface IERC4626Custom {
event Deposit(address indexed sender, address indexed owner, uint256 assets, uint256 shares);
event Withdraw(
address indexed sender, address indexed receiver, address indexed owner, uint256 assets, uint256 shares
);
/**
* @dev Attempted to deposit more assets than the max amount for `receiver`.
*/
error ERC4626ExceededMaxDeposit(address receiver, uint256 assets, uint256 max);
/**
* @dev Attempted to mint more shares than the max amount for `receiver`.
*/
error ERC4626ExceededMaxMint(address receiver, uint256 shares, uint256 max);
/**
* @dev Attempted to withdraw more assets than the max amount for `receiver`.
*/
error ERC4626ExceededMaxWithdraw(address owner, uint256 assets, uint256 max);
/**
* @dev Attempted to redeem more shares than the max amount for `receiver`.
*/
error ERC4626ExceededMaxRedeem(address owner, uint256 shares, uint256 max);
/**
* @dev Exceeds the slippage set by user in the non-standard withdrawWithSlippageProtection function.
*/
error ERC4626WithdrawSlippageExceeded(address owner, uint256 sharesRequired, uint256 maxBurntShares);
/**
* @dev Exceeds the slippage set by user in the non-standard redeemWithSlippageProtection function.
*/
error ERC4626RedeemSlippageExceeded(address owner, uint256 netAssets, uint256 minNetAssets);
/**
* @dev Returns the address of the underlying token used for the Vault for accounting, depositing, and withdrawing.
*
* - MUST be an ERC-20 token contract.
* - MUST NOT revert.
*/
function asset() external view returns (address assetTokenAddress);
/**
* @dev Returns the total amount of the underlying asset that is “managed” by Vault.
*
* - SHOULD include any compounding that occurs from yield.
* - MUST be inclusive of any fees that are charged against assets in the Vault.
* - MUST NOT revert.
*/
function totalAssets() external view returns (uint256 totalManagedAssets);
/**
* @dev Returns the amount of shares that the Vault would exchange for the amount of assets provided, in an ideal
* scenario where all the conditions are met.
*
* - MUST NOT be inclusive of any fees that are charged against assets in the Vault.
* - MUST NOT show any variations depending on the caller.
* - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.
* - MUST NOT revert.
*
* NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the
* “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and
* from.
*/
function convertToShares(uint256 assets) external view returns (uint256 shares);
/**
* @dev Returns the amount of assets that the Vault would exchange for the amount of shares provided, in an ideal
* scenario where all the conditions are met.
*
* - MUST NOT be inclusive of any fees that are charged against assets in the Vault.
* - MUST NOT show any variations depending on the caller.
* - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.
* - MUST NOT revert.
*
* NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the
* “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and
* from.
*/
function convertToAssets(uint256 shares) external view returns (uint256 assets);
/**
* @dev Returns the maximum amount of the underlying asset that can be deposited into the Vault for the receiver,
* through a deposit call.
*
* - MUST return a limited value if receiver is subject to some deposit limit.
* - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of assets that may be deposited.
* - MUST NOT revert.
*/
function maxDeposit(address receiver) external view returns (uint256 maxAssets);
/**
* @dev Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given
* current on-chain conditions.
*
* - MUST return as close to and no more than the exact amount of Vault shares that would be minted in a deposit
* call in the same transaction. I.e. deposit should return the same or more shares as previewDeposit if called
* in the same transaction.
* - MUST NOT account for deposit limits like those returned from maxDeposit and should always act as though the
* deposit would be accepted, regardless if the user has enough tokens approved, etc.
* - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.
* - MUST NOT revert.
*
* NOTE: any unfavorable discrepancy between convertToShares and previewDeposit SHOULD be considered slippage in
* share price or some other type of condition, meaning the depositor will lose assets by depositing.
*/
function previewDeposit(uint256 assets) external view returns (uint256 shares);
/**
* @dev Mints shares Vault shares to receiver by depositing exactly amount of underlying tokens.
*
* - MUST emit the Deposit event.
* - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
* deposit execution, and are accounted for during deposit.
* - MUST revert if all of assets cannot be deposited (due to deposit limit being reached, slippage, the user not
* approving enough underlying tokens to the Vault contract, etc).
*
* NOTE: No pre-approval required, but native tokens (of `assets` amount) must be sent as msg.value.
*/
function deposit(uint256 assets, address receiver) external payable returns (uint256 shares);
/**
* @dev Returns the maximum amount of the Vault shares that can be minted for the receiver, through a mint call.
* - MUST return a limited value if receiver is subject to some mint limit.
* - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of shares that may be minted.
* - MUST NOT revert.
*/
function maxMint(address receiver) external view returns (uint256 maxShares);
/**
* @dev Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given
* current on-chain conditions.
*
* - MUST return as close to and no fewer than the exact amount of assets that would be deposited in a mint call
* in the same transaction. I.e. mint should return the same or fewer assets as previewMint if called in the
* same transaction.
* - MUST NOT account for mint limits like those returned from maxMint and should always act as though the mint
* would be accepted, regardless if the user has enough tokens approved, etc.
* - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.
* - MUST NOT revert.
*
* NOTE: any unfavorable discrepancy between convertToAssets and previewMint SHOULD be considered slippage in
* share price or some other type of condition, meaning the depositor will lose assets by minting.
*/
function previewMint(uint256 shares) external view returns (uint256 assets);
/**
* @dev Mints exactly shares Vault shares to receiver by depositing amount of underlying tokens.
*
* - MUST emit the Deposit event.
* - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the mint
* execution, and are accounted for during mint.
* - MUST revert if all of shares cannot be minted (due to deposit limit being reached, slippage, the user not
* approving enough underlying tokens to the Vault contract, etc).
*
* NOTE: No pre-approval required, but native tokens (of `assets` amount) must be sent as msg.value.
*/
function mint(uint256 shares, address receiver) external payable returns (uint256 assets);
/**
* @dev Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the
* Vault, through a withdraw call.
*
* - MUST return a limited value if owner is subject to some withdrawal limit or timelock.
* - MUST NOT revert.
*/
function maxWithdraw(address owner) external view returns (uint256 maxAssets);
/**
* @dev Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block,
* given current on-chain conditions.
*
* - MUST return as close to and no fewer than the exact amount of Vault shares that would be burned in a withdraw
* call in the same transaction. I.e. withdraw should return the same or fewer shares as previewWithdraw if
* called
* in the same transaction.
* - MUST NOT account for withdrawal limits like those returned from maxWithdraw and should always act as though
* the withdrawal would be accepted, regardless if the user has enough shares, etc.
* - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.
* - MUST NOT revert.
*
* NOTE: any unfavorable discrepancy between convertToShares and previewWithdraw SHOULD be considered slippage in
* share price or some other type of condition, meaning the depositor will lose assets by depositing.
*/
function previewWithdraw(uint256 assets) external view returns (uint256 shares);
/**
* @dev Burns shares from owner and sends exactly assets of underlying tokens to receiver.
*
* - MUST emit the Withdraw event.
* - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
* withdraw execution, and are accounted for during withdraw.
* - MUST revert if all of assets cannot be withdrawn (due to withdrawal limit being reached, slippage, the owner
* not having enough shares, etc).
*
* Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed.
* Those methods should be performed separately.
*/
function withdraw(uint256 assets, address receiver, address owner) external returns (uint256 shares);
/**
* @dev Burns shares from owner and sends exactly assets of underlying tokens to receiver with slippage protection.
* @param assets The amount of assets to withdraw.
* @param receiver The address to receive the assets.
* @param owner The address of the owner of the shares.
* @param maxBurntShares The maximum amount of shares that can be burnt.
* @return shares The amount of shares withdrawn.
*/
function withdrawWithSlippageProtection(
uint256 assets,
address receiver,
address owner,
uint256 maxBurntShares
)
external
returns (uint256 shares);
/**
* @dev Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault,
* through a redeem call.
*
* - MUST return a limited value if owner is subject to some withdrawal limit or timelock.
* - MUST return balanceOf(owner) if owner is not subject to any withdrawal limit or timelock.
* - MUST NOT revert.
*/
function maxRedeem(address owner) external view returns (uint256 maxShares);
/**
* @dev Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block,
* given current on-chain conditions.
*
* - MUST return as close to and no more than the exact amount of assets that would be withdrawn in a redeem call
* in the same transaction. I.e. redeem should return the same or more assets as previewRedeem if called in the
* same transaction.
* - MUST NOT account for redemption limits like those returned from maxRedeem and should always act as though the
* redemption would be accepted, regardless if the user has enough shares, etc.
* - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.
* - MUST NOT revert.
*
* NOTE: any unfavorable discrepancy between convertToAssets and previewRedeem SHOULD be considered slippage in
* share price or some other type of condition, meaning the depositor will lose assets by redeeming.
*/
function previewRedeem(uint256 shares) external view returns (uint256 assets);
/**
* @dev Burns exactly shares from owner and sends assets of underlying tokens to receiver.
*
* - MUST emit the Withdraw event.
* - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
* redeem execution, and are accounted for during redeem.
* - MUST revert if all of shares cannot be redeemed (due to withdrawal limit being reached, slippage, the owner
* not having enough shares, etc).
*
* NOTE: some implementations will require pre-requesting to the Vault before a withdrawal may be performed.
* Those methods should be performed separately.
*/
function redeem(uint256 shares, address receiver, address owner) external returns (uint256 assets);
/**
* @dev Burns shares from owner and sends assets of underlying tokens to receiver with slippage protection.
* @param shares The amount of shares to redeem.
* @param receiver The address to receive the assets.
* @param owner The address of the owner of the shares.
* @param minAmountOut The minimum amount of assets that must be received.
* @return assets The amount of assets redeemed.
*/
function redeemWithSlippageProtection(
uint256 shares,
address receiver,
address owner,
uint256 minAmountOut
)
external
returns (uint256 assets);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { IERC20Permit } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol";
import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import { IERC20Errors } from "@openzeppelin/contracts/interfaces/draft-IERC6093.sol";
/// @dev Aggregation of OpenZeppelin's interfaces required for an ERC20 token with permit.
interface IERC20Full is IERC20, IERC20Metadata, IERC20Errors, IERC20Permit {
/**
* @dev Permit deadline has expired.
*/
error ERC2612ExpiredSignature(uint256 deadline);
/**
* @dev Mismatched signature.
*/
error ERC2612InvalidSigner(address signer, address owner);
}//SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.8.28 <0.9.0;
// 3 types of mutually exclusive shMON balances:
// - Uncommitted: shMON that can be transferred freely.
// - Committed: shMON that is committed to a Policy and has not yet started uncommitting.
// - Uncommitting: shMON that is in the process of uncommitting from a Policy.
// NOTE: we do not track an account's total uncommitting balance.
// Would need to add array of policies active per account to calc total uncommitting balance.
struct Balance {
uint128 uncommitted; // Account's uncommitted shMON balance
uint128 committed; // Account's committed shMON balance across all policies
}
struct Supply {
uint128 total;
uint128 committedTotal;
}
struct CommittedData {
uint128 committed; // Account's committed amount in the current Policy (excl. uncommitting)
uint128 minCommitted; // Account's minimum committed amount in the current Policy
}
struct UncommittingData {
uint128 uncommitting; // Account's uncommitting amount in the current Policy
uint48 uncommitStartBlock; // Block at which account last started uncommitting
uint80 placeholder; // Placeholder for future use
}
struct TopUpData {
uint128 totalPeriodTopUps; // Sum of all top-ups in the current top-up period
uint48 topUpPeriodStartBlock; // block.number of start of last top-up period
uint80 placeholder; // Placeholder for future use
}
struct TopUpSettings {
uint128 maxTopUpPerPeriod; // Max uncommitted shMON allowed per top-up of committed
uint32 topUpPeriodDuration; // Duration of the top-up period, in blocks
uint96 placeholder; // Placeholder for future use
}
struct Policy {
uint48 escrowDuration; // Uncommitting period of the Policy
bool active; // Whether the Policy is active or not
address primaryAgent; // Most frequently-calling agent (for gas efficiency)
}
// For HoldsLib - never used in storage
struct PolicyAccount {
uint64 policyID;
address account;
}
struct UncommitApproval {
address completor; // Account allowed to `completeUncommit()` on user's behalf
uint96 shares; // Max shares where uncommit can be completed on user's behalf
}
enum Delivery {
Committed,
Uncommitted,
Underlying
}
// ================================================== //
// CapitalAllocator Types //
// ================================================== //
// Lightweight view snapshot assembled from validator epoch/reward trackers.
struct ValidatorStats {
bool isActive;
address coinbase;
uint64 lastEpoch;
uint128 targetStakeAmount;
uint128 rewardsPayableLast;
uint128 earnedRevenueLast;
uint128 rewardsPayableCurrent;
uint128 earnedRevenueCurrent;
}
// UnstakeRequest at the User <> ShMonad level
struct UserUnstakeRequest {
uint128 amountMon; // Amount in MON being unstaked
uint64 completionEpoch; // Epoch when the unstake request is able to be completed
}
// ================================================== //
// StakeTracker Types //
// ================================================== //
struct Epoch {
uint64 epoch;
uint8 withdrawalId;
bool hasWithdrawal;
bool hasDeposit;
bool crankedInBoundaryPeriod;
bool wasCranked; // refers to the placeholder validator if global, or the validator if specific
bool frozen;
bool closed;
uint128 targetStakeAmount;
}
struct RevenueSmoother {
uint120 earnedRevenueLast;
uint64 epochChangeBlockNumber;
}
struct ValidatorDataStorage {
uint64 epoch;
uint64 id;
bool isActive;
bool inActiveSet_Current;
bool inActiveSet_Last;
}
struct ValidatorData {
uint64 epoch;
uint64 id;
bool isPlaceholder;
bool isActive;
bool inActiveSet_Current;
bool inActiveSet_Last;
address coinbase;
}
struct AdminValues {
uint64 internalEpoch;
uint16 targetLiquidityPercentage;
uint16 incentiveAlignmentPercentage;
uint16 stakingCommission;
uint16 boostCommissionRate; // measured in basis points
uint128 totalZeroYieldPayable; // liability, total zero-yield tranche funds, incl owner commission
}
struct FeeParams {
uint128 mRay; // slope rate (RAY)
uint128 cRay; // y-intercept/base fee rate (RAY)
}
struct CashFlows {
uint120 queueToStake; // MON units,
uint120 queueForUnstake; // MON units
bool alwaysTrue; // Avoids storing a zero slot to save gas on future writes
}
struct AtomicCapital {
uint128 allocatedAmount;
uint128 distributedAmount;
}
struct StakingEscrow {
uint120 pendingStaking; // MON units
uint120 pendingUnstaking; // MON units
bool alwaysTrue; // Avoids storing a zero slot to save gas on future writes
}
struct Revenue {
uint120 allocatedRevenue; // MON units used to offset the atomic unstaking pool
uint120 earnedRevenue; // MON units retained by protocol
bool alwaysTrue; // Avoids storing a zero slot to save gas on future writes
}
struct PendingBoost {
uint120 rewardsPayable; // MON units earmarked for validator payouts
uint120 earnedRevenue; // MON units retained by protocol
bool alwaysTrue; // Avoids storing a zero slot to save gas on future writes
}
struct WorkingCapital {
uint128 stakedAmount;
uint128 reservedAmount; // portion reserved for validator payments or withdrawals
}
struct CurrentLiabilities {
uint128 rewardsPayable; // to validators
uint128 redemptionsPayable; // illiquid
}
enum CashFlowType {
Goodwill, // The null value
Deposit,
Revenue,
AllocationReduction
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC20/extensions/IERC20Permit.sol)
pragma solidity >=0.4.16;
/**
* @dev Interface of the ERC-20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[ERC-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC-20 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.
*
* ==== Security Considerations
*
* There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
* expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
* considered as an intention to spend the allowance in any specific way. The second is that because permits have
* built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
* take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
* generally recommended is:
*
* ```solidity
* function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
* try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
* doThing(..., value);
* }
*
* function doThing(..., uint256 value) public {
* token.safeTransferFrom(msg.sender, address(this), value);
* ...
* }
* ```
*
* Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
* `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
* {SafeERC20-safeTransferFrom}).
*
* Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
* contracts should have entry points that don't rely on permit.
*/
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].
*
* CAUTION: See Security Considerations above.
*/
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);
}{
"remappings": [
"@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
"@solady/=lib/solady/src/",
"@fastlane/contracts/=lib/fastlane-contracts/src/",
"forge-std/=lib/forge-std/src/",
"@openzeppelin-upgradeable/contracts/=lib/fastlane-contracts/lib/openzeppelin-contracts-upgradeable/contracts/",
"@openzeppelin/contracts-upgradeable/=lib/fastlane-contracts/lib/openzeppelin-contracts-upgradeable/contracts/",
"account-abstraction-v7/=lib/fastlane-contracts/lib/account-abstraction-v7/",
"account-abstraction-v8/=lib/fastlane-contracts/lib/account-abstraction-v8/",
"erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
"fastlane-contracts/=lib/fastlane-contracts/",
"halmos-cheatcodes/=lib/openzeppelin-contracts/lib/halmos-cheatcodes/src/",
"openzeppelin-contracts-upgradeable/=lib/fastlane-contracts/lib/openzeppelin-contracts-upgradeable/",
"openzeppelin-contracts/=lib/openzeppelin-contracts/",
"solady/=lib/solady/src/"
],
"optimizer": {
"enabled": true,
"runs": 50
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "ipfs",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "cancun",
"viaIR": true
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"_shMonad","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"inputs":[],"name":"RelayBidAmountIsZero","type":"error"},{"inputs":[],"name":"RelayCannotBeSelf","type":"error"},{"inputs":[],"name":"RelayCannotBeZero","type":"error"},{"inputs":[],"name":"RelayInvalidSender","type":"error"},{"inputs":[],"name":"RelayMustBeSelf","type":"error"},{"inputs":[],"name":"RelayNotActiveValidator","type":"error"},{"inputs":[{"internalType":"uint256","name":"bidAmount","type":"uint256"},{"internalType":"uint256","name":"actualAmount","type":"uint256"}],"name":"RelayNotRepaid","type":"error"},{"inputs":[],"name":"RelayPermissionSenderNotOrigin","type":"error"},{"inputs":[],"name":"RelayProxyIsTimelocked","type":"error"},{"inputs":[],"name":"RelayProxyUpdateInvalid","type":"error"},{"inputs":[],"name":"RelayShMonadDepositFailed","type":"error"},{"inputs":[],"name":"RelayTargetBlockNumberExceeded","type":"error"},{"inputs":[],"name":"RelayTaskSubmissionFailed","type":"error"},{"inputs":[],"name":"RelayTxHashMustBeZero","type":"error"},{"inputs":[],"name":"RelayTxHashesCountInvalid","type":"error"},{"inputs":[],"name":"RelayTxHashesCountIsZero","type":"error"},{"inputs":[],"name":"RelayUnapprovedReentrancy","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"searcher","type":"address"},{"indexed":false,"internalType":"bytes","name":"reason","type":"bytes"}],"name":"RelayBidFailed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"payor","type":"address"},{"indexed":true,"internalType":"uint64","name":"validatorId","type":"uint64"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"RelayFeeCollected","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"RelayWithdrawStuckERC20","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"RelayWithdrawStuckNativeToken","type":"event"},{"stateMutability":"payable","type":"fallback"},{"inputs":[],"name":"POLICY_ID","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SHMONAD","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"msgSender","type":"address"},{"internalType":"uint256","name":"bidAmount","type":"uint256"},{"internalType":"bool","name":"payBidOnFail","type":"bool"},{"internalType":"address","name":"searcherToAddress","type":"address"},{"internalType":"bytes","name":"searcherCallData","type":"bytes"}],"name":"bidWrapper","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"bids","outputs":[{"internalType":"uint128","name":"amount","type":"uint128"},{"internalType":"uint64","name":"blockNumber","type":"uint64"},{"internalType":"enum BidType","name":"bidType","type":"uint8"},{"internalType":"uint16","name":"bidCount","type":"uint16"},{"internalType":"bool","name":"executable","type":"bool"},{"internalType":"bool","name":"won","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"bidAmount","type":"uint256"},{"internalType":"bytes32[]","name":"txHashes","type":"bytes32[]"},{"internalType":"uint256","name":"targetBlockNumber","type":"uint256"},{"internalType":"bool","name":"executeOnLoss","type":"bool"},{"internalType":"bool","name":"payBidOnFail","type":"bool"},{"internalType":"address","name":"searcherToAddress","type":"address"},{"internalType":"bytes","name":"searcherCallData","type":"bytes"}],"name":"flashExecutionBid","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_tokenAddress","type":"address"}],"name":"withdrawStuckERC20","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawStuckNativeToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]Contract Creation Code
60c0806040523461013657602081611622803803809161001f828561016f565b83398101031261013657516001600160a01b0381169081810361013657331561015c575f8054336001600160a01b0319821681178355604051949290916001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09080a36001600255801561014d576024835f819360209560805263190b3bf960e11b8352603c60048401525af1908115610142575f916100fc575b5060a05260405161147b90816101a782396080518181816104560152818161123c0152611342015260a0518181816104a4015261138c0152f35b90506020813d60201161013a575b816101176020938361016f565b8101031261013657516001600160401b0381168103610136575f6100c2565b5f80fd5b3d915061010a565b6040513d5f823e3d90fd5b6303c9cfe560e41b5f5260045ffd5b631e4fbdf760e01b5f525f60045260245ffd5b601f909101601f19168101906001600160401b0382119082101761019257604052565b634e487b7160e01b5f52604160045260245ffdfe60806040526004361015610010575b005b5f3560e01c80630c7abd22146100af5780633963510b146100aa578063715018a6146100a55780638da5cb5b146100a05780638f98eeda1461009b578063ae61ebe914610096578063db4ef9a614610091578063dcaefc471461008c578063eae82567146100875763f2fde38b0361000e57610655565b6105e1565b6104c8565b610485565b610441565b6103d7565b61033a565b6102e3565b6101e8565b61014d565b801515036100be57565b5f80fd5b606435906100cf826100b4565b565b608435906100cf826100b4565b60a435906001600160a01b03821682036100be57565b600435906001600160a01b03821682036100be57565b606435906001600160a01b03821682036100be57565b9181601f840112156100be578235916001600160401b0383116100be57602083818601950101116100be57565b60e03660031901126100be576004356024356001600160401b0381116100be57366023820112156100be578060040135906001600160401b0382116100be573660248360051b830101116100be576044356101a66100c2565b6101ae6100d1565b906101b76100de565b9260c435956001600160401b0387116100be5761000e976101de6024983690600401610120565b98909701906106f0565b346100be5760203660031901126100be576102016100f4565b610209611142565b6102176001600254146106da565b336002556040516370a0823160e01b81523060048201526001600160a01b03821691602082602481865afa9182156102d4575f926102a3575b5081610261575b61000e6001600255565b8161026d913390611168565b60405190815233907f926728cf17c245d940beb38b77b4f8bc09b8d54bb7564f5d53598c12607fe93490602090a35f8080610257565b6102c691925060203d6020116102cd575b6102be818361087d565b810190610988565b905f610250565b503d6102b4565b6108fa565b5f9103126100be57565b346100be575f3660031901126100be576102fb611142565b5f80546001600160a01b0319811682556001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b346100be575f3660031901126100be575f546040516001600160a01b039091168152602090f35b6005111561036b57565b634e487b7160e01b5f52602160045260245ffd5b90600582101561036b5752565b9491936103c361ffff929897946001600160401b0360a09760c08a019b60018060801b03168a52166020890152604088019061037f565b166060850152151560808401521515910152565b346100be5760203660031901126100be576004355f52600160205261043d60405f2054604051918160ff849360e01c169060ff8160d81c169061ffff8160c81c169060ff8160c01c16906001600160401b038160801c169060018060801b03168761038c565b0390f35b346100be575f3660031901126100be576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b346100be575f3660031901126100be5760206040516001600160401b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b60a03660031901126100be576104dc6100f4565b60243590604435906104ed826100b4565b6104f561010a565b926084356001600160401b0381116100be575f61051961054f923690600401610120565b9096610526303314610997565b61053034476109d4565b97604051809581948293632ce0e73b60e21b8452898b60048601610a54565b039134906001600160a01b03165af19081156102d4575f905f926105bc575b5015806105b4575b6105ac575061058861000e94476109d4565b91818311156111f6579161059f826105a5926109d4565b83611126565b80916111f6565b602081519101fd5b508315610576565b90506105da91503d805f833e6105d2818361087d565b8101906109e1565b905f61056e565b346100be575f3660031901126100be576105f9611142565b6106076001600254146106da565b336002554780610619575b6001600255005b6106238133611126565b6040519081527f81a2140e856260bc8f016ea5945b13611668fb5f3fcf1ab0beb261e09149cfaa60203392a25f610612565b346100be5760203660031901126100be5761066e6100f4565b610676611142565b6001600160a01b031680156106c7575f80546001600160a01b03198116831782556001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09080a3005b631e4fbdf760e01b5f525f60045260245ffd5b156106e157565b63079edbb160e11b5f5260045ffd5b9196979361071861072b9297969461070c6001600254146106da565b3360025543111561081d565b610723831515610833565b878784610b9d565b966107396080890151151590565b156107ff57303b156100be575f9361076791604051968795869563dcaefc4760e01b875233600488016108be565b038134305af190816107e5575b506107dc576107bd9261078561092f565b7fbe877e9fd96672907d8df80a513570f0220a522480acbe4c0e8b2c63af9fbec2604051806107b533948261095e565b0390a2610efb565b346107cd575b6100cf6001600255565b6107d73433611126565b6107c3565b6107d792611051565b806107f35f6107f99361087d565b806102d9565b5f610774565b505050505061080d92610efb565b34156107c3576107d73433611126565b1561082457565b63aa3b452960e01b5f5260045ffd5b1561083a57565b632a76c70760e01b5f5260045ffd5b634e487b7160e01b5f52604160045260245ffd5b60c081019081106001600160401b0382111761087857604052565b610849565b90601f801991011681019081106001600160401b0382111761087857604052565b908060209392818452848401375f828201840152601f01601f1916010190565b6001600160a01b039182168152602081019290925291151560408201529116606082015260a0608082018190526108f79391019161089e565b90565b6040513d5f823e3d90fd5b604051906100cf60c08361087d565b6001600160401b03811161087857601f01601f191660200190565b3d15610959573d9061094082610914565b9161094e604051938461087d565b82523d5f602084013e565b606090565b602060409281835280519182918282860152018484015e5f828201840152601f01601f1916010190565b908160209103126100be575190565b1561099e57565b630fb823ad60e21b5f5260045ffd5b634e487b7160e01b5f52601160045260245ffd5b5f198101919082116109cf57565b6109ad565b919082039182116109cf57565b91906040838203126100be5782516109f8816100b4565b926020810151906001600160401b0382116100be570181601f820112156100be57805190610a2582610914565b92610a33604051948561087d565b828452602083830101116100be57815f9260208093018386015e8301015290565b6108f7949260609260018060a01b031682526020820152816040820152019161089e565b60405190610a858261085d565b5f60a0838281528260208201528260408201528260608201528260808201520152565b15610aaf57565b634a1f748160e01b5f5260045ffd5b9190811015610ace5760051b0190565b634e487b7160e01b5f52603260045260245ffd5b15610ae957565b634efcea5960e01b5f5260045ffd5b90604051610b058161085d565b91546001600160801b0381168352608081901c6001600160401b0316602084015260c081901c60ff16908390600583101561036b57610b7e60a0916100cf946040850152610b6161ffff8260c81c16606086019061ffff169052565b610b7560d882901c60ff1615156080860152565b60e01c60ff1690565b1515910152565b919060206040840193610b9981600161037f565b0152565b610bdb9093929193610bad610a78565b50610bb9831515610aa8565b610bd6610bcf610bc8856109c1565b8588610abe565b3515610ae2565b611420565b9260018203610cf1575050610c26610c216040516020810190610c1081610c024385610b85565b03601f19810183528261087d565b5190205f52600160205260405f2090565b610af8565b606081015161ffff169081610c7e57505050610c52610c43610905565b6001600160801b039092168252565b436001600160401b03166020820152600160408201525f6060820152600160808201525f60a082015290565b60a001516108f7929015801592610cde9284610ce7575b50610cd590610cb4610ca5610905565b6001600160801b039098168852565b436001600160401b031660208801526001604088015261ffff166060870152565b15156080850152565b151560a0830152565b9150610cd5610c95565b600282959392145f14610dee57610d18610d09610905565b6001600160801b039095168552565b436001600160401b03166020850152600260408501525f60608501526080840190610d438260019052565b60a08501935f85525f5b610d56886109c1565b811015610de45780610d80610c21610d716001948c88610abe565b355f52600160205260405f2090565b610d99610d92606083015161ffff1690565b61ffff1690565b15610dde57610daf610dab8951151590565b1590565b9081610dd0575b50610dc2575b01610d4d565b600187528515158552610dbc565b60a00151151590505f610db6565b50610dbc565b5095505050505090565b637bbc9b2b60e01b5f5260045ffd5b61ffff60019116019061ffff82116109cf57565b81518154602084015167ffffffffffffffff60801b60809190911b166001600160801b039092166001600160c01b031990911617178155604082015191610e5783610361565b600583101561036b5760a0610ee1916100cf9484549060ff60c01b9060c01b169060ff60c01b1916178455610eb0610e94606083015161ffff1690565b855461ffff60c81b191660c89190911b61ffff60c81b16178555565b610eda610ec06080830151151590565b855460ff60d81b191690151560d81b60ff60d81b16178555565b0151151590565b815460ff60e01b191690151560e01b60ff60e01b16179055565b919060016040840151610f0d81610361565b610f1681610361565b03610fc25750505f60a06100cf92610fa46040516020810190610f3d81610c024385610b85565b519020610f55610c21825f52600160205260405f2090565b90610f65610dab86840151151590565b610fac575b610f9f90610f9160608401610f89610f84825161ffff1690565b610dfd565b61ffff169052565b5f52600160205260405f2090565b610e11565b019015159052565b85151585850152839150610f9f905b9050610f6a565b5f5b610fcd836109c1565b811015611042578061102a610fe56001938686610abe565b35610ffb610c21825f52600160205260405f2090565b9061100c610dab60a0840151151590565b61103057610f9f90610f9160608401610f89610f84825161ffff1690565b01610fc4565b5f60a0890152879150610f9f90610fbb565b5050505f60a06100cf92610fa4565b91906001604084015161106381610361565b61106c81610361565b03611094575050600160a06100cf92610fa46040516020810190610f3d81610c024385610b85565b5f5b61109f836109c1565b81101561111657806110fc6110b76001938686610abe565b356110cd610c21825f52600160205260405f2090565b906110de610dab60a0840151151590565b61110257610f9f90610f9160608401610f89610f84825161ffff1690565b01611096565b84151560a0890152879150610f9f90610fbb565b505050600160a06100cf92610fa4565b5f80809338935af11561113557565b63b12d13eb5f526004601cfd5b5f546001600160a01b0316330361115557565b63118cdaa760e01b5f523360045260245ffd5b919060145260345263a9059cbb60601b5f5260205f6044601082855af1908160015f5114161561119b575b50505f603452565b3b153d1710156111ac575f80611193565b6390b8ec185f526004601cfd5b156111c2575050565b6353dc88d960e01b5f5260045260245260445ffd5b908160209103126100be57516001600160401b03811681036100be5790565b9291909180611417575b611340575b80828061121293146111b9565b604051637dd6585f60e11b81526020816004815f6110005af19081156102d4575f91611311575b507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316803b156100be5760405163e5cdc7c760e01b81526001600160401b03838116600483015267016345785d8a000060248301527f17f45ae963f99b4d1929ba44f0acd3d95021fd4ccf3ec9af9d3dcdb7417274bd9390929091905f908290604490829089905af190816112fd575b506112f8576112e08441611126565b60405193845216926001600160a01b031691602090a3565b6112e0565b806107f35f61130b9361087d565b5f6112d1565b611333915060203d602011611339575b61132b818361087d565b8101906111d7565b5f611239565b503d611321565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169061137590836109d4565b90803b156100be57604051637466200960e01b81527f00000000000000000000000000000000000000000000000000000000000000006001600160401b031660048201526001600160a01b038516602482015230604482015260648101929092525f60848301819052600160a484015290829060c490829084905af180156102d457611403575b5080611205565b806107f35f6114119361087d565b5f6113fc565b50818110611200565b600160801b811015611438576001600160801b031690565b6335278d125f526004601cfdfea2646970667358221220f2c8f82f71fd40935d534b694d4373f0127b64288406d0282ec961b271edaf9c64736f6c634300081c00330000000000000000000000001b68626dca36c7fe922fd2d55e4f631d962de19c
Deployed Bytecode
0x60806040526004361015610010575b005b5f3560e01c80630c7abd22146100af5780633963510b146100aa578063715018a6146100a55780638da5cb5b146100a05780638f98eeda1461009b578063ae61ebe914610096578063db4ef9a614610091578063dcaefc471461008c578063eae82567146100875763f2fde38b0361000e57610655565b6105e1565b6104c8565b610485565b610441565b6103d7565b61033a565b6102e3565b6101e8565b61014d565b801515036100be57565b5f80fd5b606435906100cf826100b4565b565b608435906100cf826100b4565b60a435906001600160a01b03821682036100be57565b600435906001600160a01b03821682036100be57565b606435906001600160a01b03821682036100be57565b9181601f840112156100be578235916001600160401b0383116100be57602083818601950101116100be57565b60e03660031901126100be576004356024356001600160401b0381116100be57366023820112156100be578060040135906001600160401b0382116100be573660248360051b830101116100be576044356101a66100c2565b6101ae6100d1565b906101b76100de565b9260c435956001600160401b0387116100be5761000e976101de6024983690600401610120565b98909701906106f0565b346100be5760203660031901126100be576102016100f4565b610209611142565b6102176001600254146106da565b336002556040516370a0823160e01b81523060048201526001600160a01b03821691602082602481865afa9182156102d4575f926102a3575b5081610261575b61000e6001600255565b8161026d913390611168565b60405190815233907f926728cf17c245d940beb38b77b4f8bc09b8d54bb7564f5d53598c12607fe93490602090a35f8080610257565b6102c691925060203d6020116102cd575b6102be818361087d565b810190610988565b905f610250565b503d6102b4565b6108fa565b5f9103126100be57565b346100be575f3660031901126100be576102fb611142565b5f80546001600160a01b0319811682556001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b346100be575f3660031901126100be575f546040516001600160a01b039091168152602090f35b6005111561036b57565b634e487b7160e01b5f52602160045260245ffd5b90600582101561036b5752565b9491936103c361ffff929897946001600160401b0360a09760c08a019b60018060801b03168a52166020890152604088019061037f565b166060850152151560808401521515910152565b346100be5760203660031901126100be576004355f52600160205261043d60405f2054604051918160ff849360e01c169060ff8160d81c169061ffff8160c81c169060ff8160c01c16906001600160401b038160801c169060018060801b03168761038c565b0390f35b346100be575f3660031901126100be576040517f0000000000000000000000001b68626dca36c7fe922fd2d55e4f631d962de19c6001600160a01b03168152602090f35b346100be575f3660031901126100be5760206040516001600160401b037f0000000000000000000000000000000000000000000000000000000000000004168152f35b60a03660031901126100be576104dc6100f4565b60243590604435906104ed826100b4565b6104f561010a565b926084356001600160401b0381116100be575f61051961054f923690600401610120565b9096610526303314610997565b61053034476109d4565b97604051809581948293632ce0e73b60e21b8452898b60048601610a54565b039134906001600160a01b03165af19081156102d4575f905f926105bc575b5015806105b4575b6105ac575061058861000e94476109d4565b91818311156111f6579161059f826105a5926109d4565b83611126565b80916111f6565b602081519101fd5b508315610576565b90506105da91503d805f833e6105d2818361087d565b8101906109e1565b905f61056e565b346100be575f3660031901126100be576105f9611142565b6106076001600254146106da565b336002554780610619575b6001600255005b6106238133611126565b6040519081527f81a2140e856260bc8f016ea5945b13611668fb5f3fcf1ab0beb261e09149cfaa60203392a25f610612565b346100be5760203660031901126100be5761066e6100f4565b610676611142565b6001600160a01b031680156106c7575f80546001600160a01b03198116831782556001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09080a3005b631e4fbdf760e01b5f525f60045260245ffd5b156106e157565b63079edbb160e11b5f5260045ffd5b9196979361071861072b9297969461070c6001600254146106da565b3360025543111561081d565b610723831515610833565b878784610b9d565b966107396080890151151590565b156107ff57303b156100be575f9361076791604051968795869563dcaefc4760e01b875233600488016108be565b038134305af190816107e5575b506107dc576107bd9261078561092f565b7fbe877e9fd96672907d8df80a513570f0220a522480acbe4c0e8b2c63af9fbec2604051806107b533948261095e565b0390a2610efb565b346107cd575b6100cf6001600255565b6107d73433611126565b6107c3565b6107d792611051565b806107f35f6107f99361087d565b806102d9565b5f610774565b505050505061080d92610efb565b34156107c3576107d73433611126565b1561082457565b63aa3b452960e01b5f5260045ffd5b1561083a57565b632a76c70760e01b5f5260045ffd5b634e487b7160e01b5f52604160045260245ffd5b60c081019081106001600160401b0382111761087857604052565b610849565b90601f801991011681019081106001600160401b0382111761087857604052565b908060209392818452848401375f828201840152601f01601f1916010190565b6001600160a01b039182168152602081019290925291151560408201529116606082015260a0608082018190526108f79391019161089e565b90565b6040513d5f823e3d90fd5b604051906100cf60c08361087d565b6001600160401b03811161087857601f01601f191660200190565b3d15610959573d9061094082610914565b9161094e604051938461087d565b82523d5f602084013e565b606090565b602060409281835280519182918282860152018484015e5f828201840152601f01601f1916010190565b908160209103126100be575190565b1561099e57565b630fb823ad60e21b5f5260045ffd5b634e487b7160e01b5f52601160045260245ffd5b5f198101919082116109cf57565b6109ad565b919082039182116109cf57565b91906040838203126100be5782516109f8816100b4565b926020810151906001600160401b0382116100be570181601f820112156100be57805190610a2582610914565b92610a33604051948561087d565b828452602083830101116100be57815f9260208093018386015e8301015290565b6108f7949260609260018060a01b031682526020820152816040820152019161089e565b60405190610a858261085d565b5f60a0838281528260208201528260408201528260608201528260808201520152565b15610aaf57565b634a1f748160e01b5f5260045ffd5b9190811015610ace5760051b0190565b634e487b7160e01b5f52603260045260245ffd5b15610ae957565b634efcea5960e01b5f5260045ffd5b90604051610b058161085d565b91546001600160801b0381168352608081901c6001600160401b0316602084015260c081901c60ff16908390600583101561036b57610b7e60a0916100cf946040850152610b6161ffff8260c81c16606086019061ffff169052565b610b7560d882901c60ff1615156080860152565b60e01c60ff1690565b1515910152565b919060206040840193610b9981600161037f565b0152565b610bdb9093929193610bad610a78565b50610bb9831515610aa8565b610bd6610bcf610bc8856109c1565b8588610abe565b3515610ae2565b611420565b9260018203610cf1575050610c26610c216040516020810190610c1081610c024385610b85565b03601f19810183528261087d565b5190205f52600160205260405f2090565b610af8565b606081015161ffff169081610c7e57505050610c52610c43610905565b6001600160801b039092168252565b436001600160401b03166020820152600160408201525f6060820152600160808201525f60a082015290565b60a001516108f7929015801592610cde9284610ce7575b50610cd590610cb4610ca5610905565b6001600160801b039098168852565b436001600160401b031660208801526001604088015261ffff166060870152565b15156080850152565b151560a0830152565b9150610cd5610c95565b600282959392145f14610dee57610d18610d09610905565b6001600160801b039095168552565b436001600160401b03166020850152600260408501525f60608501526080840190610d438260019052565b60a08501935f85525f5b610d56886109c1565b811015610de45780610d80610c21610d716001948c88610abe565b355f52600160205260405f2090565b610d99610d92606083015161ffff1690565b61ffff1690565b15610dde57610daf610dab8951151590565b1590565b9081610dd0575b50610dc2575b01610d4d565b600187528515158552610dbc565b60a00151151590505f610db6565b50610dbc565b5095505050505090565b637bbc9b2b60e01b5f5260045ffd5b61ffff60019116019061ffff82116109cf57565b81518154602084015167ffffffffffffffff60801b60809190911b166001600160801b039092166001600160c01b031990911617178155604082015191610e5783610361565b600583101561036b5760a0610ee1916100cf9484549060ff60c01b9060c01b169060ff60c01b1916178455610eb0610e94606083015161ffff1690565b855461ffff60c81b191660c89190911b61ffff60c81b16178555565b610eda610ec06080830151151590565b855460ff60d81b191690151560d81b60ff60d81b16178555565b0151151590565b815460ff60e01b191690151560e01b60ff60e01b16179055565b919060016040840151610f0d81610361565b610f1681610361565b03610fc25750505f60a06100cf92610fa46040516020810190610f3d81610c024385610b85565b519020610f55610c21825f52600160205260405f2090565b90610f65610dab86840151151590565b610fac575b610f9f90610f9160608401610f89610f84825161ffff1690565b610dfd565b61ffff169052565b5f52600160205260405f2090565b610e11565b019015159052565b85151585850152839150610f9f905b9050610f6a565b5f5b610fcd836109c1565b811015611042578061102a610fe56001938686610abe565b35610ffb610c21825f52600160205260405f2090565b9061100c610dab60a0840151151590565b61103057610f9f90610f9160608401610f89610f84825161ffff1690565b01610fc4565b5f60a0890152879150610f9f90610fbb565b5050505f60a06100cf92610fa4565b91906001604084015161106381610361565b61106c81610361565b03611094575050600160a06100cf92610fa46040516020810190610f3d81610c024385610b85565b5f5b61109f836109c1565b81101561111657806110fc6110b76001938686610abe565b356110cd610c21825f52600160205260405f2090565b906110de610dab60a0840151151590565b61110257610f9f90610f9160608401610f89610f84825161ffff1690565b01611096565b84151560a0890152879150610f9f90610fbb565b505050600160a06100cf92610fa4565b5f80809338935af11561113557565b63b12d13eb5f526004601cfd5b5f546001600160a01b0316330361115557565b63118cdaa760e01b5f523360045260245ffd5b919060145260345263a9059cbb60601b5f5260205f6044601082855af1908160015f5114161561119b575b50505f603452565b3b153d1710156111ac575f80611193565b6390b8ec185f526004601cfd5b156111c2575050565b6353dc88d960e01b5f5260045260245260445ffd5b908160209103126100be57516001600160401b03811681036100be5790565b9291909180611417575b611340575b80828061121293146111b9565b604051637dd6585f60e11b81526020816004815f6110005af19081156102d4575f91611311575b507f0000000000000000000000001b68626dca36c7fe922fd2d55e4f631d962de19c6001600160a01b0316803b156100be5760405163e5cdc7c760e01b81526001600160401b03838116600483015267016345785d8a000060248301527f17f45ae963f99b4d1929ba44f0acd3d95021fd4ccf3ec9af9d3dcdb7417274bd9390929091905f908290604490829089905af190816112fd575b506112f8576112e08441611126565b60405193845216926001600160a01b031691602090a3565b6112e0565b806107f35f61130b9361087d565b5f6112d1565b611333915060203d602011611339575b61132b818361087d565b8101906111d7565b5f611239565b503d611321565b7f0000000000000000000000001b68626dca36c7fe922fd2d55e4f631d962de19c6001600160a01b03169061137590836109d4565b90803b156100be57604051637466200960e01b81527f00000000000000000000000000000000000000000000000000000000000000046001600160401b031660048201526001600160a01b038516602482015230604482015260648101929092525f60848301819052600160a484015290829060c490829084905af180156102d457611403575b5080611205565b806107f35f6114119361087d565b5f6113fc565b50818110611200565b600160801b811015611438576001600160801b031690565b6335278d125f526004601cfdfea2646970667358221220f2c8f82f71fd40935d534b694d4373f0127b64288406d0282ec961b271edaf9c64736f6c634300081c0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000001b68626dca36c7fe922fd2d55e4f631d962de19c
-----Decoded View---------------
Arg [0] : _shMonad (address): 0x1B68626dCa36c7fE922fD2d55E4f631d962dE19c
-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 0000000000000000000000001b68626dca36c7fe922fd2d55e4f631d962de19c
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in MON
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.