Source Code
Overview
MON Balance
MON Value
$0.00Advanced mode: Intended for advanced users or developers and will display all Internal Transactions including zero value transfers.
Latest 25 internal transactions (View All)
Advanced mode:
| Parent Transaction Hash | Block | From | To | ||||
|---|---|---|---|---|---|---|---|
| 50872605 | 21 secs ago | 0 MON | |||||
| 50872605 | 21 secs ago | 0 MON | |||||
| 50872605 | 21 secs ago | 0 MON | |||||
| 50872605 | 21 secs ago | 0 MON | |||||
| 50872593 | 26 secs ago | 0 MON | |||||
| 50872593 | 26 secs ago | 0 MON | |||||
| 50872593 | 26 secs ago | 0 MON | |||||
| 50872593 | 26 secs ago | 0 MON | |||||
| 50872591 | 27 secs ago | 0 MON | |||||
| 50872591 | 27 secs ago | 0 MON | |||||
| 50872591 | 27 secs ago | 0 MON | |||||
| 50872591 | 27 secs ago | 0 MON | |||||
| 50872591 | 27 secs ago | 0 MON | |||||
| 50872591 | 27 secs ago | 0 MON | |||||
| 50872591 | 27 secs ago | 0 MON | |||||
| 50872591 | 27 secs ago | 0 MON | |||||
| 50872559 | 40 secs ago | 0 MON | |||||
| 50872559 | 40 secs ago | 0 MON | |||||
| 50872559 | 40 secs ago | 0 MON | |||||
| 50872559 | 40 secs ago | 0 MON | |||||
| 50872559 | 40 secs ago | 0 MON | |||||
| 50872559 | 40 secs ago | 0 MON | |||||
| 50872559 | 40 secs ago | 0 MON | |||||
| 50872559 | 40 secs ago | 0 MON | |||||
| 50872556 | 41 secs ago | 0 MON |
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Name:
MarginAccount
Compiler Version
v0.8.30+commit.73712a01
Optimization Enabled:
Yes with 1000 runs
Other Settings:
prague EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: BUSL-1.1
// ░█████░ ░█████▓ ███
// ███████ ▓███████▒ ███▒
// ███████ █████████▒ ███▒
// ███████ ██████████░ ███▒ ████ ████ ████ ▓████████ ░███░ ░███▓
// ░████▒██████▒░ ███▒ ████ ████ ████ ▓████████ ▒███▒ ░███▓
// ███▒ ▒████ ████ ████ ▓████ ▒███░ ░███▓
// ▓████▒███████▓ █████████ ████ ████ ▓███░ ▒███░ ░███▓
// ███████ ██████████▒ █████████▒ ████ ████ ▓███ ░███▒ ░███▒
// ███████ █████████▒ ███▒ ████▓ ▓███▒ ░████ ▓███ ████ ████░
// ███████ ▓███████▒ ███▒ ▒████ ███████████ ▓███ ░██████████▒
// ▒▓▓▓▓ ▒▓▓▓▓▒ ▓██ ███▒ ░█████░ ██▓ ▒████▒
pragma solidity ^0.8.20;
// ============ External Imports ============
import {SafeTransferLib} from "solady/src/utils/SafeTransferLib.sol";
import {Ownable} from "solady/src/auth/Ownable.sol";
import {Initializable} from "solady/src/utils/Initializable.sol";
import {UUPSUpgradeable} from "solady/src/utils/UUPSUpgradeable.sol";
// ============ Internal Library Imports ============
import {ERC2771Context} from "./libraries/ERC2771Context.sol";
import {MarginAccountErrors} from "./libraries/Errors.sol";
// ============ Internal Interface Imports ============
import {IMarginAccount} from "./interfaces/IMarginAccount.sol";
contract MarginAccount is IMarginAccount, ERC2771Context, Ownable, Initializable, UUPSUpgradeable {
using SafeTransferLib for address;
address trustedForwarder;
bool protocolPaused;
mapping(bytes32 => uint256) public balances;
mapping(address => bool) public verifiedMarket;
address routerContractAddress;
address feeCollector;
address private constant NATIVE = 0x0000000000000000000000000000000000000000;
constructor() {
_disableInitializers();
}
// ============ admin ================
function toggleProtocolState(bool _state) external onlyOwner {
require(protocolPaused != _state, MarginAccountErrors.ProtocolStateNotChanged());
protocolPaused = _state;
emit ProtocolStateUpdated(_state);
}
function setFeeCollector(address _feeCollector) external onlyOwner {
require(feeCollector != _feeCollector, MarginAccountErrors.FeeCollectorNotChanged());
feeCollector = _feeCollector;
emit FeeCollectorUpdated(_feeCollector);
}
// ============ auth =================
function _guardInitializeOwner() internal pure override returns (bool guard) {
guard = true;
}
function _authorizeUpgrade(address) internal view override {
_checkOwner();
}
modifier onlyRouter() {
require(msg.sender == routerContractAddress, MarginAccountErrors.OnlyRouterAllowed());
_;
}
modifier protocolActive() {
require(!protocolPaused, MarginAccountErrors.ProtocolPaused());
_;
}
function isTrustedForwarder(address forwarder) public view override returns (bool) {
return forwarder == trustedForwarder;
}
// ============ initializer =================
function initialize(address _owner, address _router, address _feeCollector, address _trustedForwarder)
public
initializer
{
require(
_owner != address(0) && _router != address(0) && _feeCollector != address(0)
&& _trustedForwarder != address(0),
MarginAccountErrors.ZeroAddressNotAllowed()
);
_initializeOwner(_owner);
routerContractAddress = _router;
feeCollector = _feeCollector;
trustedForwarder = _trustedForwarder;
}
// ============ router functions=================
/**
* @dev This function allows the router to register an official verified market.
* @param _marketAddress Address of the orderbook of the market.
*/
function updateMarkets(address _marketAddress) external onlyRouter protocolActive {
verifiedMarket[_marketAddress] = true;
}
// ============ market functions=================
/**
* @dev This function is only callable by a verified market. Verified markets consume user balances.
* @param _user address of the user whose account should be debited.
* @param _token address of token to be debited.
* @param _amount amount to debit.
*/
function debitUser(address _user, address _token, uint256 _amount) external protocolActive {
require(verifiedMarket[msg.sender], MarginAccountErrors.OnlyVerifiedMarketsAllowed());
require(balances[_accountKey(_user, _token)] >= _amount, MarginAccountErrors.InsufficientBalance());
balances[_accountKey(_user, _token)] -= _amount;
}
/**
* @dev This function is only callable by a verified market. Verified markets credit user balances.
* @param _user address of the user whose account should be credited.
* @param _token address of token to be credit.
* @param _amount amount to credit.
* @param _useMargin whether to use margin or not - if not, user will receive token through an erc20 transfer
*/
function creditUser(address _user, address _token, uint256 _amount, bool _useMargin) external protocolActive {
require(verifiedMarket[msg.sender], MarginAccountErrors.OnlyVerifiedMarketsAllowed());
if (_useMargin) {
balances[_accountKey(_user, _token)] += _amount;
} else {
if (_token != NATIVE) {
_token.safeTransfer(_user, _amount);
} else {
_user.safeTransferETH(_amount);
}
}
}
/**
* @dev This function is only callable by a verified market. Verified markets credit a bunch of users the tokens.
* @param _encodedData Address of the user whose account should be credited.
*/
function creditUsersEncoded(bytes calldata _encodedData) external protocolActive {
require(verifiedMarket[msg.sender], MarginAccountErrors.OnlyVerifiedMarketsAllowed());
uint256 offset = 0;
while (offset < _encodedData.length) {
(address _user, address _token, uint256 _amount, bool _useMargin) =
abi.decode(_encodedData[offset:offset + 128], (address, address, uint256, bool));
offset += 128;
if (_useMargin) {
balances[_accountKey(_user, _token)] += _amount;
} else {
if (_token != NATIVE) {
_token.safeTransfer(_user, _amount);
} else {
_user.safeTransferETH(_amount);
}
}
}
}
/**
* @dev This function allows a verified market to register fee collected
* @param _assetA Address of first asset
* @param _feeA Fee amount of _assetA to be credited
* @param _assetB Address of second asset
* @param _feeB Fee amount of _assetB to be credited
*/
function creditFee(address _assetA, uint256 _feeA, address _assetB, uint256 _feeB) external protocolActive {
require(verifiedMarket[msg.sender], MarginAccountErrors.OnlyVerifiedMarketsAllowed());
balances[_accountKey(feeCollector, _assetA)] += _feeA;
balances[_accountKey(feeCollector, _assetB)] += _feeB;
}
// ============ user functions=================
/**
* @dev This function allows a user to claim all of their tokens from the margin account.
* @param _tokens Array of tokens to claim.
*/
function batchWithdrawMaxTokens(address[] calldata _tokens) external protocolActive {
uint256 _balance;
for (uint256 i = 0; i < _tokens.length; i++) {
_balance = balances[_accountKey(_msgSender(), _tokens[i])];
balances[_accountKey(_msgSender(), _tokens[i])] = 0;
if (_balance > 0) {
if (_tokens[i] == NATIVE) {
_msgSender().safeTransferETH(_balance);
} else {
_tokens[i].safeTransfer(_msgSender(), _balance);
}
}
emit Withdrawal(_msgSender(), _tokens[i], _balance);
}
}
/**
* @dev Function for EOAs to deposit tokens on behalf of a user.
* @param _user Address of the user whose account should be credited.
* @param _token address of token to be credit.
* @param _amount amount to credit.
*/
function deposit(address _user, address _token, uint256 _amount) external payable protocolActive {
require(_user != address(0), MarginAccountErrors.ZeroAddressNotAllowed());
if (_token == NATIVE) {
require(msg.value == _amount, MarginAccountErrors.NativeAssetMismatch());
balances[_accountKey(_user, NATIVE)] += _amount;
} else {
require(msg.value == 0, MarginAccountErrors.NativeAssetMismatch());
balances[_accountKey(_user, _token)] += _amount;
_token.safeTransferFrom(_msgSender(), address(this), _amount);
}
emit Deposit(_user, _token, _amount);
}
/**
* @dev Function for users to withdraw their assets.
* @param _amount amount to withdraw.
* @param _token address of token to be withdrawn.
*/
function withdraw(uint256 _amount, address _token) external protocolActive {
require(_amount <= balances[_accountKey(_msgSender(), _token)], MarginAccountErrors.InsufficientBalance());
balances[_accountKey(_msgSender(), _token)] -= _amount;
if (_token == NATIVE) {
_msgSender().safeTransferETH(_amount);
} else {
_token.safeTransfer(_msgSender(), _amount);
}
emit Withdrawal(_msgSender(), _token, _amount);
}
/**
* @dev Function to check balance of a user for a given token.
* @param _user user addresss
* @param _token tokenA addresss
*/
function getBalance(address _user, address _token) external view returns (uint256) {
return balances[_accountKey(_user, _token)];
}
/**
* Function to calculate key of balances.
* @param _user user addresss
* @param _token tokenA addresss
*/
function _accountKey(address _user, address _token) internal pure returns (bytes32) {
return keccak256(abi.encodePacked(_user, _token));
}
receive() external payable {}
}// 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;
/// @dev The canonical address of the `SELFDESTRUCT` ETH mover.
/// See: https://gist.github.com/Vectorized/1cb8ad4cf393b1378e08f23f79bd99fa
/// [Etherscan](https://etherscan.io/address/0x00000000000073c48c8055bD43D1A53799176f0D)
address internal constant ETH_MOVER = 0x00000000000073c48c8055bD43D1A53799176f0D;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* 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)
}
}
/// @dev Force transfers ETH to `to`, without triggering the fallback (if any).
/// This method attempts to use a separate contract to send via `SELFDESTRUCT`,
/// and upon failure, deploys a minimal vault to accrue the ETH.
function safeMoveETH(address to, uint256 amount) internal returns (address vault) {
/// @solidity memory-safe-assembly
assembly {
to := shr(96, shl(96, to)) // Clean upper 96 bits.
for { let mover := ETH_MOVER } iszero(eq(to, address())) {} {
let selfBalanceBefore := selfbalance()
if or(lt(selfBalanceBefore, amount), eq(to, mover)) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
revert(0x1c, 0x04)
}
if extcodesize(mover) {
let balanceBefore := balance(to) // Check via delta, in case `SELFDESTRUCT` is bricked.
mstore(0x00, to)
pop(call(gas(), mover, amount, 0x00, 0x20, codesize(), 0x00))
// If `address(to).balance >= amount + balanceBefore`, skip vault workflow.
if iszero(lt(balance(to), add(amount, balanceBefore))) { break }
// Just in case `SELFDESTRUCT` is changed to not revert and do nothing.
if lt(selfBalanceBefore, selfbalance()) { invalid() }
}
let m := mload(0x40)
// If the mover is missing or bricked, deploy a minimal vault
// that withdraws all ETH to `to` when being called only by `to`.
// forgefmt: disable-next-item
mstore(add(m, 0x20), 0x33146025575b600160005260206000f35b3d3d3d3d47335af1601a5760003dfd)
mstore(m, or(to, shl(160, 0x6035600b3d3960353df3fe73)))
// Compute and store the bytecode hash.
mstore8(0x00, 0xff) // Write the prefix.
mstore(0x35, keccak256(m, 0x40))
mstore(0x01, shl(96, address())) // Deployer.
mstore(0x15, 0) // Salt.
vault := keccak256(0x00, 0x55)
pop(call(gas(), vault, amount, codesize(), 0x00, codesize(), 0x00))
// The vault returns a single word on success. Failure reverts with empty data.
if iszero(returndatasize()) {
if iszero(create2(0, m, 0x40, 0)) { revert(codesize(), codesize()) } // For gas estimation.
}
mstore(0x40, m) // Restore the free memory pointer.
break
}
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* 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 Simple single owner authorization mixin.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/auth/Ownable.sol)
///
/// @dev Note:
/// This implementation does NOT auto-initialize the owner to `msg.sender`.
/// You MUST call the `_initializeOwner` in the constructor / initializer.
///
/// While the ownable portion follows
/// [EIP-173](https://eips.ethereum.org/EIPS/eip-173) for compatibility,
/// the nomenclature for the 2-step ownership handover may be unique to this codebase.
abstract contract Ownable {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The caller is not authorized to call the function.
error Unauthorized();
/// @dev The `newOwner` cannot be the zero address.
error NewOwnerIsZeroAddress();
/// @dev The `pendingOwner` does not have a valid handover request.
error NoHandoverRequest();
/// @dev Cannot double-initialize.
error AlreadyInitialized();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EVENTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The ownership is transferred from `oldOwner` to `newOwner`.
/// This event is intentionally kept the same as OpenZeppelin's Ownable to be
/// compatible with indexers and [EIP-173](https://eips.ethereum.org/EIPS/eip-173),
/// despite it not being as lightweight as a single argument event.
event OwnershipTransferred(address indexed oldOwner, address indexed newOwner);
/// @dev An ownership handover to `pendingOwner` has been requested.
event OwnershipHandoverRequested(address indexed pendingOwner);
/// @dev The ownership handover to `pendingOwner` has been canceled.
event OwnershipHandoverCanceled(address indexed pendingOwner);
/// @dev `keccak256(bytes("OwnershipTransferred(address,address)"))`.
uint256 private constant _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE =
0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0;
/// @dev `keccak256(bytes("OwnershipHandoverRequested(address)"))`.
uint256 private constant _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE =
0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d;
/// @dev `keccak256(bytes("OwnershipHandoverCanceled(address)"))`.
uint256 private constant _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE =
0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* STORAGE */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The owner slot is given by:
/// `bytes32(~uint256(uint32(bytes4(keccak256("_OWNER_SLOT_NOT")))))`.
/// It is intentionally chosen to be a high value
/// to avoid collision with lower slots.
/// The choice of manual storage layout is to enable compatibility
/// with both regular and upgradeable contracts.
bytes32 internal constant _OWNER_SLOT =
0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927;
/// The ownership handover slot of `newOwner` is given by:
/// ```
/// mstore(0x00, or(shl(96, user), _HANDOVER_SLOT_SEED))
/// let handoverSlot := keccak256(0x00, 0x20)
/// ```
/// It stores the expiry timestamp of the two-step ownership handover.
uint256 private constant _HANDOVER_SLOT_SEED = 0x389a75e1;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* INTERNAL FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Override to return true to make `_initializeOwner` prevent double-initialization.
function _guardInitializeOwner() internal pure virtual returns (bool guard) {}
/// @dev Initializes the owner directly without authorization guard.
/// This function must be called upon initialization,
/// regardless of whether the contract is upgradeable or not.
/// This is to enable generalization to both regular and upgradeable contracts,
/// and to save gas in case the initial owner is not the caller.
/// For performance reasons, this function will not check if there
/// is an existing owner.
function _initializeOwner(address newOwner) internal virtual {
if (_guardInitializeOwner()) {
/// @solidity memory-safe-assembly
assembly {
let ownerSlot := _OWNER_SLOT
if sload(ownerSlot) {
mstore(0x00, 0x0dc149f0) // `AlreadyInitialized()`.
revert(0x1c, 0x04)
}
// Clean the upper 96 bits.
newOwner := shr(96, shl(96, newOwner))
// Store the new value.
sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner))))
// Emit the {OwnershipTransferred} event.
log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)
}
} else {
/// @solidity memory-safe-assembly
assembly {
// Clean the upper 96 bits.
newOwner := shr(96, shl(96, newOwner))
// Store the new value.
sstore(_OWNER_SLOT, newOwner)
// Emit the {OwnershipTransferred} event.
log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)
}
}
}
/// @dev Sets the owner directly without authorization guard.
function _setOwner(address newOwner) internal virtual {
if (_guardInitializeOwner()) {
/// @solidity memory-safe-assembly
assembly {
let ownerSlot := _OWNER_SLOT
// Clean the upper 96 bits.
newOwner := shr(96, shl(96, newOwner))
// Emit the {OwnershipTransferred} event.
log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)
// Store the new value.
sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner))))
}
} else {
/// @solidity memory-safe-assembly
assembly {
let ownerSlot := _OWNER_SLOT
// Clean the upper 96 bits.
newOwner := shr(96, shl(96, newOwner))
// Emit the {OwnershipTransferred} event.
log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)
// Store the new value.
sstore(ownerSlot, newOwner)
}
}
}
/// @dev Throws if the sender is not the owner.
function _checkOwner() internal view virtual {
/// @solidity memory-safe-assembly
assembly {
// If the caller is not the stored owner, revert.
if iszero(eq(caller(), sload(_OWNER_SLOT))) {
mstore(0x00, 0x82b42900) // `Unauthorized()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Returns how long a two-step ownership handover is valid for in seconds.
/// Override to return a different value if needed.
/// Made internal to conserve bytecode. Wrap it in a public function if needed.
function _ownershipHandoverValidFor() internal view virtual returns (uint64) {
return 48 * 3600;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* PUBLIC UPDATE FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Allows the owner to transfer the ownership to `newOwner`.
function transferOwnership(address newOwner) public payable virtual onlyOwner {
/// @solidity memory-safe-assembly
assembly {
if iszero(shl(96, newOwner)) {
mstore(0x00, 0x7448fbae) // `NewOwnerIsZeroAddress()`.
revert(0x1c, 0x04)
}
}
_setOwner(newOwner);
}
/// @dev Allows the owner to renounce their ownership.
function renounceOwnership() public payable virtual onlyOwner {
_setOwner(address(0));
}
/// @dev Request a two-step ownership handover to the caller.
/// The request will automatically expire in 48 hours (172800 seconds) by default.
function requestOwnershipHandover() public payable virtual {
unchecked {
uint256 expires = block.timestamp + _ownershipHandoverValidFor();
/// @solidity memory-safe-assembly
assembly {
// Compute and set the handover slot to `expires`.
mstore(0x0c, _HANDOVER_SLOT_SEED)
mstore(0x00, caller())
sstore(keccak256(0x0c, 0x20), expires)
// Emit the {OwnershipHandoverRequested} event.
log2(0, 0, _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE, caller())
}
}
}
/// @dev Cancels the two-step ownership handover to the caller, if any.
function cancelOwnershipHandover() public payable virtual {
/// @solidity memory-safe-assembly
assembly {
// Compute and set the handover slot to 0.
mstore(0x0c, _HANDOVER_SLOT_SEED)
mstore(0x00, caller())
sstore(keccak256(0x0c, 0x20), 0)
// Emit the {OwnershipHandoverCanceled} event.
log2(0, 0, _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE, caller())
}
}
/// @dev Allows the owner to complete the two-step ownership handover to `pendingOwner`.
/// Reverts if there is no existing ownership handover requested by `pendingOwner`.
function completeOwnershipHandover(address pendingOwner) public payable virtual onlyOwner {
/// @solidity memory-safe-assembly
assembly {
// Compute and set the handover slot to 0.
mstore(0x0c, _HANDOVER_SLOT_SEED)
mstore(0x00, pendingOwner)
let handoverSlot := keccak256(0x0c, 0x20)
// If the handover does not exist, or has expired.
if gt(timestamp(), sload(handoverSlot)) {
mstore(0x00, 0x6f5e8818) // `NoHandoverRequest()`.
revert(0x1c, 0x04)
}
// Set the handover slot to 0.
sstore(handoverSlot, 0)
}
_setOwner(pendingOwner);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* PUBLIC READ FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the owner of the contract.
function owner() public view virtual returns (address result) {
/// @solidity memory-safe-assembly
assembly {
result := sload(_OWNER_SLOT)
}
}
/// @dev Returns the expiry timestamp for the two-step ownership handover to `pendingOwner`.
function ownershipHandoverExpiresAt(address pendingOwner)
public
view
virtual
returns (uint256 result)
{
/// @solidity memory-safe-assembly
assembly {
// Compute the handover slot.
mstore(0x0c, _HANDOVER_SLOT_SEED)
mstore(0x00, pendingOwner)
// Load the handover slot.
result := sload(keccak256(0x0c, 0x20))
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* MODIFIERS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Marks a function as only callable by the owner.
modifier onlyOwner() virtual {
_checkOwner();
_;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Initializable mixin for the upgradeable contracts.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/Initializable.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/tree/master/contracts/proxy/utils/Initializable.sol)
abstract contract Initializable {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The contract is already initialized.
error InvalidInitialization();
/// @dev The contract is not initializing.
error NotInitializing();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EVENTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Triggered when the contract has been initialized.
event Initialized(uint64 version);
/// @dev `keccak256(bytes("Initialized(uint64)"))`.
bytes32 private constant _INITIALIZED_EVENT_SIGNATURE =
0xc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* STORAGE */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The default initializable slot is given by:
/// `bytes32(~uint256(uint32(bytes4(keccak256("_INITIALIZABLE_SLOT")))))`.
///
/// Bits Layout:
/// - [0] `initializing`
/// - [1..64] `initializedVersion`
bytes32 private constant _INITIALIZABLE_SLOT =
0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffbf601132;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTRUCTOR */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
constructor() {
// Construction time check to ensure that `_initializableSlot()` is not
// overridden to zero. Will be optimized away if there is no revert.
require(_initializableSlot() != bytes32(0));
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Override to return a non-zero custom storage slot if required.
function _initializableSlot() internal pure virtual returns (bytes32) {
return _INITIALIZABLE_SLOT;
}
/// @dev Guards an initializer function so that it can be invoked at most once.
///
/// You can guard a function with `onlyInitializing` such that it can be called
/// through a function guarded with `initializer`.
///
/// This is similar to `reinitializer(1)`, except that in the context of a constructor,
/// an `initializer` guarded function can be invoked multiple times.
/// This can be useful during testing and is not expected to be used in production.
///
/// Emits an {Initialized} event.
modifier initializer() virtual {
bytes32 s = _initializableSlot();
/// @solidity memory-safe-assembly
assembly {
let i := sload(s)
// Set `initializing` to 1, `initializedVersion` to 1.
sstore(s, 3)
// If `!(initializing == 0 && initializedVersion == 0)`.
if i {
// If `!(address(this).code.length == 0 && initializedVersion == 1)`.
if iszero(lt(extcodesize(address()), eq(shr(1, i), 1))) {
mstore(0x00, 0xf92ee8a9) // `InvalidInitialization()`.
revert(0x1c, 0x04)
}
s := shl(shl(255, i), s) // Skip initializing if `initializing == 1`.
}
}
_;
/// @solidity memory-safe-assembly
assembly {
if s {
// Set `initializing` to 0, `initializedVersion` to 1.
sstore(s, 2)
// Emit the {Initialized} event.
mstore(0x20, 1)
log1(0x20, 0x20, _INITIALIZED_EVENT_SIGNATURE)
}
}
}
/// @dev Guards a reinitializer function so that it can be invoked at most once.
///
/// You can guard a function with `onlyInitializing` such that it can be called
/// through a function guarded with `reinitializer`.
///
/// Emits an {Initialized} event.
modifier reinitializer(uint64 version) virtual {
bytes32 s = _initializableSlot();
/// @solidity memory-safe-assembly
assembly {
// Clean upper bits, and shift left by 1 to make space for the initializing bit.
version := shl(1, and(version, 0xffffffffffffffff))
let i := sload(s)
// If `initializing == 1 || initializedVersion >= version`.
if iszero(lt(and(i, 1), lt(i, version))) {
mstore(0x00, 0xf92ee8a9) // `InvalidInitialization()`.
revert(0x1c, 0x04)
}
// Set `initializing` to 1, `initializedVersion` to `version`.
sstore(s, or(1, version))
}
_;
/// @solidity memory-safe-assembly
assembly {
// Set `initializing` to 0, `initializedVersion` to `version`.
sstore(s, version)
// Emit the {Initialized} event.
mstore(0x20, shr(1, version))
log1(0x20, 0x20, _INITIALIZED_EVENT_SIGNATURE)
}
}
/// @dev Guards a function such that it can only be called in the scope
/// of a function guarded with `initializer` or `reinitializer`.
modifier onlyInitializing() virtual {
_checkInitializing();
_;
}
/// @dev Reverts if the contract is not initializing.
function _checkInitializing() internal view virtual {
bytes32 s = _initializableSlot();
/// @solidity memory-safe-assembly
assembly {
if iszero(and(1, sload(s))) {
mstore(0x00, 0xd7e6bcf8) // `NotInitializing()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Locks any future initializations by setting the initialized version to `2**64 - 1`.
///
/// Calling this in the constructor will prevent the contract from being initialized
/// or reinitialized. It is recommended to use this to lock implementation contracts
/// that are designed to be called through proxies.
///
/// Emits an {Initialized} event the first time it is successfully called.
function _disableInitializers() internal virtual {
bytes32 s = _initializableSlot();
/// @solidity memory-safe-assembly
assembly {
let i := sload(s)
if and(i, 1) {
mstore(0x00, 0xf92ee8a9) // `InvalidInitialization()`.
revert(0x1c, 0x04)
}
let uint64max := 0xffffffffffffffff
if iszero(eq(shr(1, i), uint64max)) {
// Set `initializing` to 0, `initializedVersion` to `2**64 - 1`.
sstore(s, shl(1, uint64max))
// Emit the {Initialized} event.
mstore(0x20, uint64max)
log1(0x20, 0x20, _INITIALIZED_EVENT_SIGNATURE)
}
}
}
/// @dev Returns the highest version that has been initialized.
function _getInitializedVersion() internal view virtual returns (uint64 version) {
bytes32 s = _initializableSlot();
/// @solidity memory-safe-assembly
assembly {
version := shr(1, sload(s))
}
}
/// @dev Returns whether the contract is currently initializing.
function _isInitializing() internal view virtual returns (bool result) {
bytes32 s = _initializableSlot();
/// @solidity memory-safe-assembly
assembly {
result := and(1, sload(s))
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import {CallContextChecker} from "./CallContextChecker.sol";
/// @notice UUPS proxy mixin.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/UUPSUpgradeable.sol)
/// @author Modified from OpenZeppelin
/// (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/proxy/utils/UUPSUpgradeable.sol)
///
/// @dev Note:
/// - This implementation is intended to be used with ERC1967 proxies.
/// See: `LibClone.deployERC1967` and related functions.
/// - This implementation is NOT compatible with legacy OpenZeppelin proxies
/// which do not store the implementation at `_ERC1967_IMPLEMENTATION_SLOT`.
abstract contract UUPSUpgradeable is CallContextChecker {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The upgrade failed.
error UpgradeFailed();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EVENTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Emitted when the proxy's implementation is upgraded.
event Upgraded(address indexed implementation);
/// @dev `keccak256(bytes("Upgraded(address)"))`.
uint256 private constant _UPGRADED_EVENT_SIGNATURE =
0xbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* STORAGE */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The ERC-1967 storage slot for the implementation in the proxy.
/// `uint256(keccak256("eip1967.proxy.implementation")) - 1`.
bytes32 internal constant _ERC1967_IMPLEMENTATION_SLOT =
0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* UUPS OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Please override this function to check if `msg.sender` is authorized
/// to upgrade the proxy to `newImplementation`, reverting if not.
/// ```
/// function _authorizeUpgrade(address) internal override onlyOwner {}
/// ```
function _authorizeUpgrade(address newImplementation) internal virtual;
/// @dev Returns the storage slot used by the implementation,
/// as specified in [ERC1822](https://eips.ethereum.org/EIPS/eip-1822).
///
/// Note: The `notDelegated` modifier prevents accidental upgrades to
/// an implementation that is a proxy contract.
function proxiableUUID() public view virtual notDelegated returns (bytes32) {
// This function must always return `_ERC1967_IMPLEMENTATION_SLOT` to comply with ERC1967.
return _ERC1967_IMPLEMENTATION_SLOT;
}
/// @dev Upgrades the proxy's implementation to `newImplementation`.
/// Emits a {Upgraded} event.
///
/// Note: Passing in empty `data` skips the delegatecall to `newImplementation`.
function upgradeToAndCall(address newImplementation, bytes calldata data)
public
payable
virtual
onlyProxy
{
_authorizeUpgrade(newImplementation);
/// @solidity memory-safe-assembly
assembly {
newImplementation := shr(96, shl(96, newImplementation)) // Clears upper 96 bits.
mstore(0x00, returndatasize())
mstore(0x01, 0x52d1902d) // `proxiableUUID()`.
let s := _ERC1967_IMPLEMENTATION_SLOT
// Check if `newImplementation` implements `proxiableUUID` correctly.
if iszero(eq(mload(staticcall(gas(), newImplementation, 0x1d, 0x04, 0x01, 0x20)), s)) {
mstore(0x01, 0x55299b49) // `UpgradeFailed()`.
revert(0x1d, 0x04)
}
// Emit the {Upgraded} event.
log2(codesize(), 0x00, _UPGRADED_EVENT_SIGNATURE, newImplementation)
sstore(s, newImplementation) // Updates the implementation.
// Perform a delegatecall to `newImplementation` if `data` is non-empty.
if data.length {
// Forwards the `data` to `newImplementation` via delegatecall.
let m := mload(0x40)
calldatacopy(m, data.offset, data.length)
if iszero(delegatecall(gas(), newImplementation, m, data.length, codesize(), 0x00))
{
// Bubble up the revert if the call reverts.
returndatacopy(m, 0x00, returndatasize())
revert(m, returndatasize())
}
}
}
}
}//SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/**
* @dev Context variant with ERC2771 support.
*
* WARNING: The usage of `delegatecall` in this contract is dangerous and may result in context corruption.
* Any forwarded request to this contract triggering a `delegatecall` to itself will result in an invalid {_msgSender}
* recovery.
*/
abstract contract ERC2771Context {
function isTrustedForwarder(address) public view virtual returns (bool) {}
function _msgData() internal view returns (bytes calldata) {
uint256 calldataLength = msg.data.length;
uint256 contextSuffixLength = _contextSuffixLength();
if (isTrustedForwarder(msg.sender) && calldataLength >= contextSuffixLength) {
return msg.data[:calldataLength - contextSuffixLength];
} else {
return msg.data;
}
}
function _msgSender() internal view returns (address) {
uint256 calldataLength = msg.data.length;
uint256 contextSuffixLength = _contextSuffixLength();
if (isTrustedForwarder(msg.sender) && calldataLength >= contextSuffixLength) {
return address(bytes20(msg.data[calldataLength - contextSuffixLength:]));
} else {
return msg.sender;
}
}
/**
* @dev ERC-2771 specifies the context as being a single address (20 bytes).
*/
function _contextSuffixLength() internal pure returns (uint256) {
return 20;
}
}//SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.20;
library OrderBookErrors {
/**
* @dev Thrown when a user is not the owner and tries to execute a privileged function
*/
error Unauthorized();
/**
* @dev Thrown when a market is paused and a user tries to execute an action or if the owner passes an already existing market state for toggling
*/
error MarketStateError();
/**
* @dev Thrown when maker fee passed to initializer is too high/invalid
*/
error MarketFeeError();
/**
* @dev Thrown when minSize = 0 or maxSize < minSize
*/
error MarketSizeError();
/**
* @dev Thrown when available size is rounded down to zero
*/
error VaultSizeZero();
/**
* @dev Thrown when Kuru AMM Vault spread passed to initializer is too high or too low or is not a multiple of 10
*/
error InvalidSpread();
/**
* @dev Thrown when the inputted price while adding an order is invalid
*/
error PriceError();
/**
* @dev Thrown when vault initialization price crosses the book
*/
error VaultInitializationPriceCrossesBook();
/**
* @dev Thrown when the size inputted is invalid, i.e, < minSize or > maxSize
*/
error SizeError();
/**
* @dev Thrown when price is not divisible by tick
*/
error TickSizeError();
/**
* @dev Thrown when a post only order gets filled
*/
error PostOnlyError();
/**
* @dev Thrown when a flip order matches with a price
*/
error ProvisionError();
/**
* @dev Thrown when a non-owner tries to execute a privileged function, i.e, if non owner tries to pause/unpause a market or
* if a user tries to cancel an order that they did not place
*/
error OnlyOwnerAllowedError();
/**
* @dev Thrown when wrong interface is called for a cancel
*/
error WrongOrderTypeCancel();
/**
* @dev Thrown when cancelOrder is called on an order which is already filled or cancelled
*/
error OrderAlreadyFilledOrCancelled();
/**
* @dev Thrown when length mismatch occurs between inputted arrays
*/
error LengthMismatch();
/**
* @dev Thrown when msg.value is insufficient in market orders
*/
error NativeAssetInsufficient();
/**
* @dev Thrown when msg.value is surplus in market orders
*/
error NativeAssetSurplus();
/**
* @dev Thrown when msg.value is greater than 0 when native assets are not required
*/
error NativeAssetNotRequired();
/**
* @dev Thrown when native asset transfer fails
*/
error NativeAssetTransferFail();
/**
* @dev Thrown when IOC orders do not get filled by the market
*/
error InsufficientLiquidity();
/**
* @dev Throws when slippage is exceeded in market orders
*/
error SlippageExceeded();
/**
* @dev Thrown when quote size in uint32 overflows
*/
error TooMuchSizeFilled();
/**
* @dev Thrown when safe transfer from fails
*/
error TransferFromFailed();
/**
* @dev Thrown when the call is not made by the vault
*/
error OnlyVaultAllowed();
/**
* @dev Thrown when safe cast to uint96 fails
*/
error Uint96Overflow();
/**
* @dev Thrown when safe cast to uint32 fails
*/
error Uint32Overflow();
}
library MarginAccountErrors {
/**
* @dev Thrown when a non-router tries to update markets
*/
error OnlyRouterAllowed();
/**
* @dev Thrown when a non-verified market tries to execute a market action
*/
error OnlyVerifiedMarketsAllowed();
/**
* @dev Thrown when a user has insufficient margin account balance
*/
error InsufficientBalance();
/**
* @dev Thrown when native asset transfer fails
*/
error NativeAssetTransferFail();
/**
* @dev Thrown when msg.value is not zero when native assets are not required
*/
error NativeAssetMismatch();
/**
* @dev Thrown when zero address is passed as a parameter
*/
error ZeroAddressNotAllowed();
/**
* @dev Thrown when protocol is paused
*/
error ProtocolPaused();
/**
* @dev Thrown when protocol state is not changed
*/
error ProtocolStateNotChanged();
/**
* @dev Thrown when fee collector is not set
*/
error FeeCollectorNotChanged();
}
library RouterErrors {
/**
* @dev Thrown when zero address is passed as a parameter
*/
error ZeroAddressNotAllowed();
/**
* @dev Thrown when base and quote asset addresses are the same
*/
error BaseAndQuoteAssetSame();
/**
* @dev Thrown when market type given and token addresses are not compatible
*/
error MarketTypeMismatch();
/**
* @dev Thrown when tick size is 0
*/
error InvalidTickSize();
/**
* @dev Thrown when size precision is not a power of 10
*/
error InvalidSizePrecision();
/**
* @dev Thrown when price precision is not a power of 10
*/
error InvalidPricePrecision();
/**
* @dev Thrown when no markets are passed as input
*/
error NoMarketsPassed();
/**
* @dev Thrown when the length of market addresses, isBuy, and nativeSend arrays are not the same
*/
error LengthMismatch();
/**
* @dev Thrown when the market is invalid
*/
error InvalidMarket();
/**
* @dev Thrown when the slippage exceeds the expected value
*/
error SlippageExceeded();
/**
* @dev Thrown when the native asset transfer fails
*/
error NativeAssetTransferFail();
/**
* @dev Thrown when safe cast to uint96 fails
*/
error Uint96Overflow();
/**
* @dev Thrown when there is no change in the implementation
*/
error ImplementationNotChanged();
}
library KuruAMMVaultErrors {
/**
* @dev Thrown when a user is not the owner and tries to execute a privileged function
*/
error Unauthorized();
/**
* @dev Thrown when native token passed as argument and msg.value does not match
*/
error NativeAssetMismatch();
/**
* @dev Thrown when first deposit ask price is 0
*/
error AskPriceZero();
/**
* @dev Thrown when vault sizes are 0
*/
error InvalidVaultSize();
/**
* @dev Thrown when amount of quote tokens passed is insufficient
*/
error InsufficientQuoteToken();
/**
* @dev Thrown when insufficient liquidity is minted
*/
error InsufficientLiquidityMinted();
/**
* @dev Thrown when balance of owner is too less
*/
error InsufficientBalance();
/**
* @dev Thrown when quote used in a deposit is too low
*/
error InsufficientQuoteUsed();
/**
* @dev Thrown when native asset transfer fails
*/
error NativeAssetTransferFail();
/**
* @dev Thrown when new size exceeds partially filled size
*/
error NewSizeExceedsPartiallyFilledSize();
/**
* @dev Thrown when the amounts withdrawn are negative
*/
error NegativeAmountWithdrawn();
/**
* @dev Thrown when safe cast to uint96 fails
*/
error Uint96Overflow();
}
library KuruForwarderErrors {
/**
* @dev Thrown when zero address is passed as a parameter
*/
error ZeroAddressNotAllowed();
/**
* @dev Thrown when the signature does not match the request
*/
error SignatureMismatch();
/**
* @dev Thrown when the interface is not allowed
*/
error InterfaceNotAllowed();
/**
* @dev Thrown when the execution fails
*/
error ExecutionFailed();
/**
* @dev Thrown when the nonce is already used
*/
error NonceAlreadyUsed();
/**
* @dev Thrown when the value is insufficient
*/
error InsufficientValue();
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.20;
interface IMarginAccount {
event ProtocolStateUpdated(bool newState);
event FeeCollectorUpdated(address newFeeCollector);
event Deposit(address owner, address token, uint256 amount);
event Withdrawal(address owner, address token, uint256 amount);
function updateMarkets(address _marketAddress) external;
function deposit(address _user, address _token, uint256 _amount) external payable;
function withdraw(uint256 _amount, address _token) external;
function debitUser(address _user, address _token, uint256 _amount) external;
function creditFee(address _assetA, uint256 _feeA, address _assetB, uint256 _feeB) external;
function creditUser(address _user, address _token, uint256 _amount, bool _useMargin) external;
function creditUsersEncoded(bytes calldata _encodedData) external;
function getBalance(address _user, address _token) external view returns (uint256);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Call context checker mixin.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/CallContextChecker.sol)
contract CallContextChecker {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The call is from an unauthorized call context.
error UnauthorizedCallContext();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* IMMUTABLES */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev For checking if the context is a delegate call.
///
/// Note: To enable use cases with an immutable default implementation in the bytecode,
/// (see: ERC6551Proxy), we don't require that the proxy address must match the
/// value stored in the implementation slot, which may not be initialized.
uint256 private immutable __self = uint256(uint160(address(this)));
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CALL CONTEXT CHECKS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
// A proxy call can be either via a `delegatecall` to an implementation,
// or a 7702 call on an authority that points to a delegation.
/// @dev Returns whether the current call context is on a EIP7702 authority
/// (i.e. externally owned account).
function _onEIP7702Authority() internal view virtual returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
extcodecopy(address(), 0x00, 0x00, 0x20)
// Note: Checking that it starts with hex"ef01" is the most general and futureproof.
// 7702 bytecode is `abi.encodePacked(hex"ef01", uint8(version), address(delegation))`.
result := eq(0xef01, shr(240, mload(0x00)))
}
}
/// @dev Returns the implementation of this contract.
function _selfImplementation() internal view virtual returns (address) {
return address(uint160(__self));
}
/// @dev Returns whether the current call context is on the implementation itself.
function _onImplementation() internal view virtual returns (bool) {
return __self == uint160(address(this));
}
/// @dev Requires that the current call context is performed via a EIP7702 authority.
function _checkOnlyEIP7702Authority() internal view virtual {
if (!_onEIP7702Authority()) _revertUnauthorizedCallContext();
}
/// @dev Requires that the current call context is performed via a proxy.
function _checkOnlyProxy() internal view virtual {
if (_onImplementation()) _revertUnauthorizedCallContext();
}
/// @dev Requires that the current call context is NOT performed via a proxy.
/// This is the opposite of `checkOnlyProxy`.
function _checkNotDelegated() internal view virtual {
if (!_onImplementation()) _revertUnauthorizedCallContext();
}
/// @dev Requires that the current call context is performed via a EIP7702 authority.
modifier onlyEIP7702Authority() virtual {
_checkOnlyEIP7702Authority();
_;
}
/// @dev Requires that the current call context is performed via a proxy.
modifier onlyProxy() virtual {
_checkOnlyProxy();
_;
}
/// @dev Requires that the current call context is NOT performed via a proxy.
/// This is the opposite of `onlyProxy`.
modifier notDelegated() virtual {
_checkNotDelegated();
_;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* PRIVATE HELPERS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
function _revertUnauthorizedCallContext() private pure {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, 0x9f03a026) // `UnauthorizedCallContext()`.
revert(0x1c, 0x04)
}
}
}{
"remappings": [
"forge-std/=lib/forge-std/src/",
"@openzeppelin/=lib/openzeppelin-contracts/",
"@chainlink/=node_modules/@chainlink/",
"@eth-optimism/=node_modules/@eth-optimism/",
"ds-test/=lib/ds-test/src/",
"erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
"eth-gas-reporter/=node_modules/eth-gas-reporter/",
"halmos-cheatcodes/=lib/openzeppelin-contracts/lib/halmos-cheatcodes/src/",
"hardhat/=node_modules/hardhat/",
"openzeppelin-contracts/=lib/openzeppelin-contracts/",
"openzeppelin-solidity/=node_modules/openzeppelin-solidity/",
"solady/=node_modules/solady/"
],
"optimizer": {
"enabled": true,
"runs": 1000
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "ipfs",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "prague",
"viaIR": true
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AlreadyInitialized","type":"error"},{"inputs":[],"name":"FeeCollectorNotChanged","type":"error"},{"inputs":[],"name":"InsufficientBalance","type":"error"},{"inputs":[],"name":"InvalidInitialization","type":"error"},{"inputs":[],"name":"NativeAssetMismatch","type":"error"},{"inputs":[],"name":"NewOwnerIsZeroAddress","type":"error"},{"inputs":[],"name":"NoHandoverRequest","type":"error"},{"inputs":[],"name":"NotInitializing","type":"error"},{"inputs":[],"name":"OnlyRouterAllowed","type":"error"},{"inputs":[],"name":"OnlyVerifiedMarketsAllowed","type":"error"},{"inputs":[],"name":"ProtocolPaused","type":"error"},{"inputs":[],"name":"ProtocolStateNotChanged","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"inputs":[],"name":"UnauthorizedCallContext","type":"error"},{"inputs":[],"name":"UpgradeFailed","type":"error"},{"inputs":[],"name":"ZeroAddressNotAllowed","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newFeeCollector","type":"address"}],"name":"FeeCollectorUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"version","type":"uint64"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipHandoverCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipHandoverRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"newState","type":"bool"}],"name":"ProtocolStateUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"implementation","type":"address"}],"name":"Upgraded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Withdrawal","type":"event"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"balances","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"_tokens","type":"address[]"}],"name":"batchWithdrawMaxTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"cancelOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"completeOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"_assetA","type":"address"},{"internalType":"uint256","name":"_feeA","type":"uint256"},{"internalType":"address","name":"_assetB","type":"address"},{"internalType":"uint256","name":"_feeB","type":"uint256"}],"name":"creditFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"},{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"bool","name":"_useMargin","type":"bool"}],"name":"creditUser","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"_encodedData","type":"bytes"}],"name":"creditUsersEncoded","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"},{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"debitUser","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"},{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"deposit","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"},{"internalType":"address","name":"_token","type":"address"}],"name":"getBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address","name":"_router","type":"address"},{"internalType":"address","name":"_feeCollector","type":"address"},{"internalType":"address","name":"_trustedForwarder","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"forwarder","type":"address"}],"name":"isTrustedForwarder","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"result","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"ownershipHandoverExpiresAt","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"proxiableUUID","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"requestOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"_feeCollector","type":"address"}],"name":"setFeeCollector","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_state","type":"bool"}],"name":"toggleProtocolState","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"_marketAddress","type":"address"}],"name":"updateMarkets","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newImplementation","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"upgradeToAndCall","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"verifiedMarket","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"address","name":"_token","type":"address"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]Contract Creation Code
60a080604052346099573060805263409feecd195460018116608c5760011c6002600160401b0319016048575b61137d908161009e8239608051818181610b7b0152610c0f0152f35b6002600160411b0363409feecd19556001600160401b0360209081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29080a1602c565b63f92ee8a95f526004601cfd5b5f80fdfe6080604052600436101561001a575b3615610018575f80fd5b005b5f3560e01c8062f714ce14610fe657806301aef34914610f645780630f163fa814610de25780632569296214610d9957806349f59b6214610cfc5780634f1ef28614610bd657806352d1902d14610b6857806354d1f13d14610b24578063572b6c0514610af25780635f71a07c14610ab55780636c13925214610a215780636f5b601e14610917578063715018a6146108ae57806375eab98e146107e75780638340f549146105d95780638909aa3f146105af5780638da5cb5b14610585578063a42dce80146104d4578063d4fac45d14610494578063e004f924146103c6578063f04e283e14610379578063f2fde38b1461033e578063f8c8765e1461015b5763fee81cf40361000e57346101575760203660031901126101575761013e6110f2565b63389a75e1600c525f52602080600c2054604051908152f35b5f80fd5b34610157576080366003190112610157576101746110f2565b61017c6110dc565b610184611108565b606435906001600160a01b0382168092036101575763409feecd19938454806003875561030d575b506001600160a01b03169081158015806102fb575b806102e9575b806102e0575b156102b857638b78c6d819546102ab5785946001600160a01b0393849260ff1b8117638b78c6d819555f7f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08180a31673ffffffffffffffffffffffffffffffffffffffff1960035416176003551673ffffffffffffffffffffffffffffffffffffffff19600454161760045573ffffffffffffffffffffffffffffffffffffffff195f5416175f5561027b57005b6002905560016020527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2602080a1005b630dc149f05f526004601cfd5b7f8579befe000000000000000000000000000000000000000000000000000000005f5260045ffd5b508315156101cd565b506001600160a01b03821615156101c7565b506001600160a01b03851615156101c1565b9094600182811c14303b1015610331576001600160a01b039160ff1b1b94906101ac565b63f92ee8a95f526004601cfd5b6020366003190112610157576103526110f2565b61035a6112e7565b8060601b1561036c5761001890611303565b637448fbae5f526004601cfd5b60203660031901126101575761038d6110f2565b6103956112e7565b63389a75e1600c52805f526020600c2090815442116103b9575f6100189255611303565b636f5e88185f526004601cfd5b3461015757602036600319011261015757600435801515809103610157576103ec6112e7565b5f548160ff8260a01c1615151461046c57816020917fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff74ff00000000000000000000000000000000000000007fccccd486db37343dfe81f4fa3daf9a6b8dabbfab399d3dd2f5291fbeccd3d3079560a01b169116175f55604051908152a1005b7f4df55d9c000000000000000000000000000000000000000000000000000000005f5260045ffd5b34610157576040366003190112610157576104be6104b06110f2565b6104b86110dc565b90611226565b5f526001602052602060405f2054604051908152f35b34610157576020366003190112610157576104ed6110f2565b6104f56112e7565b6001600160a01b03600454911690816001600160a01b0382161461055d57817fe5693914d19c789bdee50a362998c0bc8d035a835f9871da5d51152f0582c34f9273ffffffffffffffffffffffffffffffffffffffff196020931617600455604051908152a1005b7f9fb4ba3a000000000000000000000000000000000000000000000000000000005f5260045ffd5b34610157575f366003190112610157576020638b78c6d819546001600160a01b0360405191168152f35b34610157576020366003190112610157576004355f526001602052602060405f2054604051908152f35b6105e236611160565b9160ff5f5460a01c166107bf576001600160a01b038116156102b8576001600160a01b038216610701578234036106d9576040519260208401906bffffffffffffffffffffffff198360601b1682525f603486015260288552606085019185831067ffffffffffffffff8411176106c5577f5548c837ab068cf56a2c2479df0882a4922fd203edb7517321831d95078c5f62956106c0936040525190205f52600160205260405f206106958282546111df565b90555b604051938493846001600160a01b036040929594938160608401971683521660208201520152565b0390a1005b634e487b7160e01b5f52604160045260245ffd5b7fcd41a9e3000000000000000000000000000000000000000000000000000000005f5260045ffd5b346106d9576107108282611226565b5f52600160205260405f206107268482546111df565b90556107306111ec565b60405190846060523060405260601b602c526f23b872dd000000000000000000000000600c5260205f6064601c82875af18060015f511416156107a1575b507f5548c837ab068cf56a2c2479df0882a4922fd203edb7517321831d95078c5f62936106c0915f606052604052610698565b3d843b151710156107b2578461076e565b637939f4245f526004601cfd5b7f44279255000000000000000000000000000000000000000000000000000000005f5260045ffd5b34610157576080366003190112610157576108006110f2565b6108086110dc565b906044359060643580151581036101575760ff5f5460a01c166107bf57335f52600260205260ff60405f205416156108865715610864579161084a9192611226565b5f52600160205261086060405f209182546111df565b9055005b6001600160a01b0383161561087c5761001892611271565b61001892506112cb565b7fe8430787000000000000000000000000000000000000000000000000000000005f5260045ffd5b5f366003190112610157576108c16112e7565b5f638b78c6d819547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a37f8000000000000000000000000000000000000000000000000000000000000000638b78c6d81955005b346101575760203660031901126101575760043567ffffffffffffffff811161015757610948903690600401611132565b9060ff5f5460a01c166107bf57335f52600260205260ff60405f20541615610886575f905b82821061097657005b6080820191828111610a0d57838311610157578101916109958361111e565b926109a26020820161111e565b60606040830135920135948515158603610157576001600160a01b03809116911692945f146109f457916109d69192611226565b5f5260016020526109ec60405f209182546111df565b90555b61096d565b8215610a03576109ef92611271565b6109ef92506112cb565b634e487b7160e01b5f52601160045260245ffd5b3461015757610a2f36611160565b919060ff5f5460a01c166107bf57335f52600260205260ff60405f2054161561088657610a5c8183611226565b5f5260016020528260405f205410610a8d57610a7791611226565b5f52600160205261086060405f2091825461119a565b7ff4d678b8000000000000000000000000000000000000000000000000000000005f5260045ffd5b34610157576020366003190112610157576001600160a01b03610ad66110f2565b165f526002602052602060ff60405f2054166040519015158152f35b34610157576020366003190112610157576020610b0d6110f2565b6001600160a01b03805f5416911614604051908152f35b5f3660031901126101575763389a75e1600c52335f525f6020600c2055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c925f80a2005b34610157575f36600319011261015757307f000000000000000000000000000000000000000000000000000000000000000003610bc95760206040517f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc8152f35b639f03a0265f526004601cfd5b604036600319011261015757610bea6110f2565b60243567ffffffffffffffff811161015757610c0a903690600401611132565b9091307f000000000000000000000000000000000000000000000000000000000000000014610bc9576001600160a01b0390610c446112e7565b163d5f526352d1902d6001527f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc602060016004601d855afa5103610cee57807fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b5f80a281817f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc55610cd157005b815f926040519485378338925af415610ce657005b3d5f823e3d90fd5b6355299b496001526004601dfd5b3461015757608036600319011261015757610d156110f2565b610d1d611108565b60ff5f5460a01c166107bf57335f52600260205260ff60405f2054161561088657610d56610d81926001600160a01b0360045416611226565b5f52600160205260405f20610d6e60243582546111df565b90556001600160a01b0360045416611226565b5f52600160205260405f2061086060643582546111df565b5f3660031901126101575763389a75e1600c52335f526202a30042016020600c2055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d5f80a2005b346101575760203660031901126101575760043567ffffffffffffffff811161015757366023820112156101575780600401359067ffffffffffffffff8211610157576024810190602436918460051b0101116101575760ff5f5460a01c166107bf575f5b828110610e5057005b600190610e71610e5e6111ec565b6104b8610e6c8488886111a7565b6111cb565b5f52816020527f2717ead6b9200dd235aad468c9809ea400fe33ac69b5bfaa6d3e90fc922b639860405f2054610eb6610ea86111ec565b6104b8610e6c868a8a6111a7565b5f52836020525f604081205580610f13575b610ed06111ec565b610f0a610ee1610e6c868a8a6111a7565b92604051938493846001600160a01b036040929594938160608401971683521660208201520152565b0390a101610e47565b6001600160a01b03610f29610e6c8589896111a7565b16610f4457610f3f81610f3a6111ec565b6112cb565b610ec8565b610f3f81610f56610e6c868a8a6111a7565b610f5e6111ec565b90611271565b3461015757602036600319011261015757610f7d6110f2565b6001600160a01b03600354163303610fbe5760ff5f5460a01c166107bf576001600160a01b03165f908152600260205260409020805460ff19166001179055005b7fe84c4d58000000000000000000000000000000000000000000000000000000005f5260045ffd5b34610157576040366003190112610157576004356110026110dc565b60ff5f5460a01c166107bf5761101f8161101a6111ec565b611226565b5f52600160205260405f20548211610a8d577f2717ead6b9200dd235aad468c9809ea400fe33ac69b5bfaa6d3e90fc922b63989161105f8261101a6111ec565b5f52600160205260405f2061107582825461119a565b90556001600160a01b0382166110c55761109181610f3a6111ec565b6106c061109c6111ec565b91604051938493846001600160a01b036040929594938160608401971683521660208201520152565b6110d7816110d16111ec565b84611271565b611091565b602435906001600160a01b038216820361015757565b600435906001600160a01b038216820361015757565b604435906001600160a01b038216820361015757565b35906001600160a01b038216820361015757565b9181601f840112156101575782359167ffffffffffffffff8311610157576020838186019501011161015757565b6060906003190112610157576004356001600160a01b038116810361015757906024356001600160a01b0381168103610157579060443590565b91908203918211610a0d57565b91908110156111b75760051b0190565b634e487b7160e01b5f52603260045260245ffd5b356001600160a01b03811681036101575790565b91908201809211610a0d57565b6001600160a01b035f541633148061121b575b15611217576013193601368111610a0d573560601c90565b3390565b5060143610156111ff565b6040516bffffffffffffffffffffffff19606092831b81166020830190815293831b1660348201526028815290810181811067ffffffffffffffff8211176106c55760405251902090565b91906014526034526fa9059cbb0000000000000000000000005f5260205f6044601082855af1908160015f511416156112ad575b50505f603452565b3b153d1710156112be575f806112a5565b6390b8ec185f526004601cfd5b5f80809338935af1156112da57565b63b12d13eb5f526004601cfd5b638b78c6d8195433036112f657565b6382b429005f526004601cfd5b6001600160a01b031680638b78c6d819547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3801560ff1b17638b78c6d8195556fea264697066735822122096b6ac64934ff51502dc1a2c057bf232bcabe4c2eddaee78a5267ecf52812a2c64736f6c634300081e0033
Deployed Bytecode
0x6080604052600436101561001a575b3615610018575f80fd5b005b5f3560e01c8062f714ce14610fe657806301aef34914610f645780630f163fa814610de25780632569296214610d9957806349f59b6214610cfc5780634f1ef28614610bd657806352d1902d14610b6857806354d1f13d14610b24578063572b6c0514610af25780635f71a07c14610ab55780636c13925214610a215780636f5b601e14610917578063715018a6146108ae57806375eab98e146107e75780638340f549146105d95780638909aa3f146105af5780638da5cb5b14610585578063a42dce80146104d4578063d4fac45d14610494578063e004f924146103c6578063f04e283e14610379578063f2fde38b1461033e578063f8c8765e1461015b5763fee81cf40361000e57346101575760203660031901126101575761013e6110f2565b63389a75e1600c525f52602080600c2054604051908152f35b5f80fd5b34610157576080366003190112610157576101746110f2565b61017c6110dc565b610184611108565b606435906001600160a01b0382168092036101575763409feecd19938454806003875561030d575b506001600160a01b03169081158015806102fb575b806102e9575b806102e0575b156102b857638b78c6d819546102ab5785946001600160a01b0393849260ff1b8117638b78c6d819555f7f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08180a31673ffffffffffffffffffffffffffffffffffffffff1960035416176003551673ffffffffffffffffffffffffffffffffffffffff19600454161760045573ffffffffffffffffffffffffffffffffffffffff195f5416175f5561027b57005b6002905560016020527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2602080a1005b630dc149f05f526004601cfd5b7f8579befe000000000000000000000000000000000000000000000000000000005f5260045ffd5b508315156101cd565b506001600160a01b03821615156101c7565b506001600160a01b03851615156101c1565b9094600182811c14303b1015610331576001600160a01b039160ff1b1b94906101ac565b63f92ee8a95f526004601cfd5b6020366003190112610157576103526110f2565b61035a6112e7565b8060601b1561036c5761001890611303565b637448fbae5f526004601cfd5b60203660031901126101575761038d6110f2565b6103956112e7565b63389a75e1600c52805f526020600c2090815442116103b9575f6100189255611303565b636f5e88185f526004601cfd5b3461015757602036600319011261015757600435801515809103610157576103ec6112e7565b5f548160ff8260a01c1615151461046c57816020917fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff74ff00000000000000000000000000000000000000007fccccd486db37343dfe81f4fa3daf9a6b8dabbfab399d3dd2f5291fbeccd3d3079560a01b169116175f55604051908152a1005b7f4df55d9c000000000000000000000000000000000000000000000000000000005f5260045ffd5b34610157576040366003190112610157576104be6104b06110f2565b6104b86110dc565b90611226565b5f526001602052602060405f2054604051908152f35b34610157576020366003190112610157576104ed6110f2565b6104f56112e7565b6001600160a01b03600454911690816001600160a01b0382161461055d57817fe5693914d19c789bdee50a362998c0bc8d035a835f9871da5d51152f0582c34f9273ffffffffffffffffffffffffffffffffffffffff196020931617600455604051908152a1005b7f9fb4ba3a000000000000000000000000000000000000000000000000000000005f5260045ffd5b34610157575f366003190112610157576020638b78c6d819546001600160a01b0360405191168152f35b34610157576020366003190112610157576004355f526001602052602060405f2054604051908152f35b6105e236611160565b9160ff5f5460a01c166107bf576001600160a01b038116156102b8576001600160a01b038216610701578234036106d9576040519260208401906bffffffffffffffffffffffff198360601b1682525f603486015260288552606085019185831067ffffffffffffffff8411176106c5577f5548c837ab068cf56a2c2479df0882a4922fd203edb7517321831d95078c5f62956106c0936040525190205f52600160205260405f206106958282546111df565b90555b604051938493846001600160a01b036040929594938160608401971683521660208201520152565b0390a1005b634e487b7160e01b5f52604160045260245ffd5b7fcd41a9e3000000000000000000000000000000000000000000000000000000005f5260045ffd5b346106d9576107108282611226565b5f52600160205260405f206107268482546111df565b90556107306111ec565b60405190846060523060405260601b602c526f23b872dd000000000000000000000000600c5260205f6064601c82875af18060015f511416156107a1575b507f5548c837ab068cf56a2c2479df0882a4922fd203edb7517321831d95078c5f62936106c0915f606052604052610698565b3d843b151710156107b2578461076e565b637939f4245f526004601cfd5b7f44279255000000000000000000000000000000000000000000000000000000005f5260045ffd5b34610157576080366003190112610157576108006110f2565b6108086110dc565b906044359060643580151581036101575760ff5f5460a01c166107bf57335f52600260205260ff60405f205416156108865715610864579161084a9192611226565b5f52600160205261086060405f209182546111df565b9055005b6001600160a01b0383161561087c5761001892611271565b61001892506112cb565b7fe8430787000000000000000000000000000000000000000000000000000000005f5260045ffd5b5f366003190112610157576108c16112e7565b5f638b78c6d819547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a37f8000000000000000000000000000000000000000000000000000000000000000638b78c6d81955005b346101575760203660031901126101575760043567ffffffffffffffff811161015757610948903690600401611132565b9060ff5f5460a01c166107bf57335f52600260205260ff60405f20541615610886575f905b82821061097657005b6080820191828111610a0d57838311610157578101916109958361111e565b926109a26020820161111e565b60606040830135920135948515158603610157576001600160a01b03809116911692945f146109f457916109d69192611226565b5f5260016020526109ec60405f209182546111df565b90555b61096d565b8215610a03576109ef92611271565b6109ef92506112cb565b634e487b7160e01b5f52601160045260245ffd5b3461015757610a2f36611160565b919060ff5f5460a01c166107bf57335f52600260205260ff60405f2054161561088657610a5c8183611226565b5f5260016020528260405f205410610a8d57610a7791611226565b5f52600160205261086060405f2091825461119a565b7ff4d678b8000000000000000000000000000000000000000000000000000000005f5260045ffd5b34610157576020366003190112610157576001600160a01b03610ad66110f2565b165f526002602052602060ff60405f2054166040519015158152f35b34610157576020366003190112610157576020610b0d6110f2565b6001600160a01b03805f5416911614604051908152f35b5f3660031901126101575763389a75e1600c52335f525f6020600c2055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c925f80a2005b34610157575f36600319011261015757307f00000000000000000000000057cf97fe1fac7d78b07e7e0761410cb2e91f0ca703610bc95760206040517f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc8152f35b639f03a0265f526004601cfd5b604036600319011261015757610bea6110f2565b60243567ffffffffffffffff811161015757610c0a903690600401611132565b9091307f00000000000000000000000057cf97fe1fac7d78b07e7e0761410cb2e91f0ca714610bc9576001600160a01b0390610c446112e7565b163d5f526352d1902d6001527f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc602060016004601d855afa5103610cee57807fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b5f80a281817f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc55610cd157005b815f926040519485378338925af415610ce657005b3d5f823e3d90fd5b6355299b496001526004601dfd5b3461015757608036600319011261015757610d156110f2565b610d1d611108565b60ff5f5460a01c166107bf57335f52600260205260ff60405f2054161561088657610d56610d81926001600160a01b0360045416611226565b5f52600160205260405f20610d6e60243582546111df565b90556001600160a01b0360045416611226565b5f52600160205260405f2061086060643582546111df565b5f3660031901126101575763389a75e1600c52335f526202a30042016020600c2055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d5f80a2005b346101575760203660031901126101575760043567ffffffffffffffff811161015757366023820112156101575780600401359067ffffffffffffffff8211610157576024810190602436918460051b0101116101575760ff5f5460a01c166107bf575f5b828110610e5057005b600190610e71610e5e6111ec565b6104b8610e6c8488886111a7565b6111cb565b5f52816020527f2717ead6b9200dd235aad468c9809ea400fe33ac69b5bfaa6d3e90fc922b639860405f2054610eb6610ea86111ec565b6104b8610e6c868a8a6111a7565b5f52836020525f604081205580610f13575b610ed06111ec565b610f0a610ee1610e6c868a8a6111a7565b92604051938493846001600160a01b036040929594938160608401971683521660208201520152565b0390a101610e47565b6001600160a01b03610f29610e6c8589896111a7565b16610f4457610f3f81610f3a6111ec565b6112cb565b610ec8565b610f3f81610f56610e6c868a8a6111a7565b610f5e6111ec565b90611271565b3461015757602036600319011261015757610f7d6110f2565b6001600160a01b03600354163303610fbe5760ff5f5460a01c166107bf576001600160a01b03165f908152600260205260409020805460ff19166001179055005b7fe84c4d58000000000000000000000000000000000000000000000000000000005f5260045ffd5b34610157576040366003190112610157576004356110026110dc565b60ff5f5460a01c166107bf5761101f8161101a6111ec565b611226565b5f52600160205260405f20548211610a8d577f2717ead6b9200dd235aad468c9809ea400fe33ac69b5bfaa6d3e90fc922b63989161105f8261101a6111ec565b5f52600160205260405f2061107582825461119a565b90556001600160a01b0382166110c55761109181610f3a6111ec565b6106c061109c6111ec565b91604051938493846001600160a01b036040929594938160608401971683521660208201520152565b6110d7816110d16111ec565b84611271565b611091565b602435906001600160a01b038216820361015757565b600435906001600160a01b038216820361015757565b604435906001600160a01b038216820361015757565b35906001600160a01b038216820361015757565b9181601f840112156101575782359167ffffffffffffffff8311610157576020838186019501011161015757565b6060906003190112610157576004356001600160a01b038116810361015757906024356001600160a01b0381168103610157579060443590565b91908203918211610a0d57565b91908110156111b75760051b0190565b634e487b7160e01b5f52603260045260245ffd5b356001600160a01b03811681036101575790565b91908201809211610a0d57565b6001600160a01b035f541633148061121b575b15611217576013193601368111610a0d573560601c90565b3390565b5060143610156111ff565b6040516bffffffffffffffffffffffff19606092831b81166020830190815293831b1660348201526028815290810181811067ffffffffffffffff8211176106c55760405251902090565b91906014526034526fa9059cbb0000000000000000000000005f5260205f6044601082855af1908160015f511416156112ad575b50505f603452565b3b153d1710156112be575f806112a5565b6390b8ec185f526004601cfd5b5f80809338935af1156112da57565b63b12d13eb5f526004601cfd5b638b78c6d8195433036112f657565b6382b429005f526004601cfd5b6001600160a01b031680638b78c6d819547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3801560ff1b17638b78c6d8195556fea264697066735822122096b6ac64934ff51502dc1a2c057bf232bcabe4c2eddaee78a5267ecf52812a2c64736f6c634300081e0033
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 ]
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.