Source Code
Latest 25 from a total of 7,053 transactions
| Transaction Hash |
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
| 0x35f10401 | 4142428 | 31 hrs ago | IN | 0 ETH | 0.00001733 | ||||
| 0x35f10401 | 4141428 | 33 hrs ago | IN | 0 ETH | 0.00041686 | ||||
| 0x35f10401 | 4140943 | 34 hrs ago | IN | 0.5 ETH | 0.00001574 | ||||
| 0x35f10401 | 4140938 | 34 hrs ago | IN | 1 ETH | 0.0000158 | ||||
| 0x35f10401 | 4140935 | 34 hrs ago | IN | 0.5 ETH | 0.0000158 | ||||
| 0x35f10401 | 4140566 | 35 hrs ago | IN | 0 ETH | 0.00001012 | ||||
| 0x35f10401 | 4138299 | 39 hrs ago | IN | 0.5 ETH | 0.0000158 | ||||
| 0x35f10401 | 4137627 | 40 hrs ago | IN | 0.5 ETH | 0.0000158 | ||||
| 0x35f10401 | 4136545 | 42 hrs ago | IN | 0.5 ETH | 0.00003985 | ||||
| 0x35f10401 | 4136507 | 42 hrs ago | IN | 0.5 ETH | 0.0000158 | ||||
| 0x35f10401 | 4130563 | 2 days ago | IN | 0.5 ETH | 0.00002232 | ||||
| 0x35f10401 | 4129152 | 2 days ago | IN | 0.5 ETH | 0.00001 | ||||
| 0x35f10401 | 4128892 | 2 days ago | IN | 0 ETH | 0.00001706 | ||||
| 0x35f10401 | 4128735 | 2 days ago | IN | 0.5 ETH | 0.0000158 | ||||
| 0x35f10401 | 4128713 | 2 days ago | IN | 0.5 ETH | 0.00001574 | ||||
| 0x35f10401 | 4128708 | 2 days ago | IN | 0.5 ETH | 0.00002043 | ||||
| 0x35f10401 | 4128702 | 2 days ago | IN | 0.5 ETH | 0.00001022 | ||||
| 0x35f10401 | 4128411 | 2 days ago | IN | 1 ETH | 0.00002043 | ||||
| 0x35f10401 | 4128391 | 2 days ago | IN | 0.5 ETH | 0.00003261 | ||||
| 0x35f10401 | 4128313 | 2 days ago | IN | 0.5 ETH | 0.00003915 | ||||
| 0x35f10401 | 4128308 | 2 days ago | IN | 0.5 ETH | 0.00002108 | ||||
| 0x35f10401 | 4128304 | 2 days ago | IN | 0.5 ETH | 0.00002042 | ||||
| 0x35f10401 | 4127885 | 2 days ago | IN | 0.5 ETH | 0.00002031 | ||||
| 0x35f10401 | 4126784 | 2 days ago | IN | 0.5 ETH | 0.00003315 | ||||
| 0x35f10401 | 4126737 | 2 days ago | IN | 0.5 ETH | 0.00003304 |
Latest 25 internal transactions (View All)
Advanced mode:
| Parent Transaction Hash | Block | From | To | |||
|---|---|---|---|---|---|---|
| 4158067 | 2 hrs ago | 0.00052939 ETH | ||||
| 4158067 | 2 hrs ago | 0.00052939 ETH | ||||
| 4157456 | 3 hrs ago | 0.00326102 ETH | ||||
| 4157456 | 3 hrs ago | 0.0000007 ETH | ||||
| 4157456 | 3 hrs ago | 0.0000007 ETH | ||||
| 4157456 | 3 hrs ago | 0.00326244 ETH | ||||
| 4151070 | 15 hrs ago | 0.02954399 ETH | ||||
| 4151070 | 15 hrs ago | 0.0000322 ETH | ||||
| 4151070 | 15 hrs ago | 0.0295762 ETH | ||||
| 4142428 | 31 hrs ago | 0.50046921 ETH | ||||
| 4142428 | 31 hrs ago | 0.50046921 ETH | ||||
| 4140943 | 34 hrs ago | 0.5 ETH | ||||
| 4140938 | 34 hrs ago | 1 ETH | ||||
| 4140935 | 34 hrs ago | 0.5 ETH | ||||
| 4140566 | 35 hrs ago | 0.00374762 ETH | ||||
| 4140566 | 35 hrs ago | 0.00374762 ETH | ||||
| 4138299 | 39 hrs ago | 0.5 ETH | ||||
| 4137627 | 40 hrs ago | 0.5 ETH | ||||
| 4136545 | 42 hrs ago | 0.5 ETH | ||||
| 4136507 | 42 hrs ago | 0.5 ETH | ||||
| 4130563 | 2 days ago | 0.5 ETH | ||||
| 4129846 | 2 days ago | 0.00022116 ETH | ||||
| 4129846 | 2 days ago | 0.00000111 ETH | ||||
| 4129846 | 2 days ago | 0.00000111 ETH | ||||
| 4129846 | 2 days ago | 0.00022339 ETH |
Cross-Chain Transactions
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:
EisenDiamond
Compiler Version
v0.8.19+commit.7dd6d404
Optimization Enabled:
Yes with 100000 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
import {LibDiamond} from "contracts/Libraries/LibDiamond.sol";
import {IDiamondCut} from "contracts/Interfaces/IDiamondCut.sol";
import {LibUtil} from "contracts/Libraries/LibUtil.sol";
import {LibPayments, Recipient, Payer} from "contracts/Libraries/LibPayments.sol";
import {TransferrableOwnership} from "contracts/Helpers/TransferrableOwnership.sol";
import {GenericErrors} from "contracts/Errors/GenericErrors.sol";
/// @title Eisen Diamond
/// @author Eisen (https://app.eisenfinance.com)
/// @notice Base EIP-2535 Diamond Proxy Contract.
/// @custom:version 1.0.0
contract EisenDiamond {
constructor(address _contractOwner, address _diamondCutFacet, address weth9, address permit2, uint256 fee) payable {
LibDiamond.setContractOwner(_contractOwner);
LibPayments.initializePayment(weth9, permit2, _contractOwner, fee);
// Add the diamondCut external function from the diamondCutFacet
IDiamondCut.FacetCut[] memory cut = new IDiamondCut.FacetCut[](1);
bytes4[] memory functionSelectors = new bytes4[](1);
functionSelectors[0] = IDiamondCut.diamondCut.selector;
cut[0] = IDiamondCut.FacetCut({
facetAddress: _diamondCutFacet,
action: IDiamondCut.FacetCutAction.Add,
functionSelectors: functionSelectors
});
LibDiamond.diamondCut(cut, address(0), "");
}
/// Modifier ///
modifier onlyOwner() {
if (msg.sender != LibDiamond.contractOwner()) {
revert GenericErrors.GenericError(GenericErrors.ONLY_CONTRACT_OWNER);
}
_;
}
/// External Methods ///
function setFee(uint256 fee) external onlyOwner {
LibPayments.setFee(fee);
}
function changeFeeCollector(address feeCollector) external onlyOwner {
LibPayments.changeFeeCollector(feeCollector);
}
// Find facet for function that is called and execute the
// function if a facet is found and return any value.
// solhint-disable-next-line no-complex-fallback
fallback() external payable {
LibDiamond.DiamondStorage storage ds;
bytes32 position = LibDiamond.DIAMOND_STORAGE_POSITION;
// get diamond storage
// solhint-disable-next-line no-inline-assembly
assembly ("memory-safe") {
ds.slot := position
}
// get facet from function selector
address facet = ds.selectorToFacetAndPosition[msg.sig].facetAddress;
if (facet == address(0)) {
revert LibDiamond.DiamondError(LibDiamond.FUNCTION_DOES_NOT_EXIST);
}
bytes4 selector = bytes4(msg.sig);
// Execute external function from facet using delegatecall and return any value.
// solhint-disable-next-line no-inline-assembly
assembly ("memory-safe") {
// copy function selector and any arguments
calldatacopy(0, 0, calldatasize())
// execute function call using the facet
let result := delegatecall(gas(), facet, 0, calldatasize(), 0, 0)
// get any return value
returndatacopy(0, 0, returndatasize())
// return any return value or error back to the caller
switch result
case 0 {
// 0xa0d0feeb == function selector of flashloan ["flashloan(address,address[],uint256[],uint256[],bytes)"]
if eq(selector, 0xa0d0feeb) {
// flashloan
return(0, returndatasize())
}
// Requires success from the function call
revert(0, returndatasize())
}
default {
return(0, returndatasize())
}
}
}
// Able to receive ether
// solhint-disable-next-line no-empty-blocks
receive() external payable {}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
import {IDiamondCut} from "contracts/Interfaces/IDiamondCut.sol";
import {LibUtil} from "contracts/Libraries/LibUtil.sol";
import {GenericErrors} from "contracts/Errors/GenericErrors.sol";
/// Implementation of EIP-2535 Diamond Standard
/// https://eips.ethereum.org/EIPS/eip-2535
library LibDiamond {
bytes32 internal constant DIAMOND_STORAGE_POSITION = keccak256("diamond.standard.diamond.storage");
// Diamond specific errors
error DiamondError(uint256 errorCode);
uint256 internal constant INCORRECT_FACET_CUT_ACTION = 1;
uint256 internal constant NO_SELECTORS_IN_FACE = 2;
uint256 internal constant FUNCTION_ALREADY_EXISTS = 3;
uint256 internal constant FACET_ADDRESS_IS_ZERO = 4;
uint256 internal constant FACET_ADDRESS_IS_NOT_ZERO = 5;
uint256 internal constant FACET_CONTAINS_NO_CODE = 6;
uint256 internal constant FUNCTION_DOES_NOT_EXIST = 7;
uint256 internal constant FUNCTION_IS_IMMUTABLE = 8;
uint256 internal constant INIT_ZERO_BUT_CALLDATA_NOT_EMPTY = 9;
uint256 internal constant CALLDATA_EMPTY_BUT_INIT_NOT_ZERO = 10;
uint256 internal constant INIT_REVERTED = 11;
// ----------------
struct FacetAddressAndPosition {
address facetAddress;
uint96 functionSelectorPosition; // position in facetFunctionSelectors.functionSelectors array
}
struct FacetFunctionSelectors {
bytes4[] functionSelectors;
uint256 facetAddressPosition; // position of facetAddress in facetAddresses array
}
struct DiamondStorage {
// maps function selector to the facet address and
// the position of the selector in the facetFunctionSelectors.selectors array
mapping(bytes4 => FacetAddressAndPosition) selectorToFacetAndPosition;
// maps facet addresses to function selectors
mapping(address => FacetFunctionSelectors) facetFunctionSelectors;
// facet addresses
address[] facetAddresses;
// Used to query if a contract implements an interface.
// Used to implement ERC-165.
mapping(bytes4 => bool) supportedInterfaces;
// owner of the contract
address contractOwner;
}
function diamondStorage() internal pure returns (DiamondStorage storage ds) {
bytes32 position = DIAMOND_STORAGE_POSITION;
// solhint-disable-next-line no-inline-assembly
assembly ("memory-safe") {
ds.slot := position
}
}
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
function setContractOwner(address _newOwner) internal {
DiamondStorage storage ds = diamondStorage();
address previousOwner = ds.contractOwner;
ds.contractOwner = _newOwner;
emit OwnershipTransferred(previousOwner, _newOwner);
}
function contractOwner() internal view returns (address contractOwner_) {
contractOwner_ = diamondStorage().contractOwner;
}
function enforceIsContractOwner() internal view {
if (msg.sender != diamondStorage().contractOwner)
revert GenericErrors.GenericError(GenericErrors.ONLY_CONTRACT_OWNER);
}
event DiamondCut(IDiamondCut.FacetCut[] _diamondCut, address _init, bytes _calldata);
// Internal function version of diamondCut
function diamondCut(IDiamondCut.FacetCut[] memory _diamondCut, address _init, bytes memory _calldata) internal {
for (uint256 facetIndex; facetIndex < _diamondCut.length; ) {
IDiamondCut.FacetCutAction action = _diamondCut[facetIndex].action;
if (action == IDiamondCut.FacetCutAction.Add) {
addFunctions(_diamondCut[facetIndex].facetAddress, _diamondCut[facetIndex].functionSelectors);
} else if (action == IDiamondCut.FacetCutAction.Replace) {
replaceFunctions(_diamondCut[facetIndex].facetAddress, _diamondCut[facetIndex].functionSelectors);
} else if (action == IDiamondCut.FacetCutAction.Remove) {
removeFunctions(_diamondCut[facetIndex].facetAddress, _diamondCut[facetIndex].functionSelectors);
} else {
revert DiamondError(INCORRECT_FACET_CUT_ACTION);
}
unchecked {
++facetIndex;
}
}
emit DiamondCut(_diamondCut, _init, _calldata);
initializeDiamondCut(_init, _calldata);
}
function addFunctions(address _facetAddress, bytes4[] memory _functionSelectors) internal {
if (_functionSelectors.length == 0) {
revert DiamondError(NO_SELECTORS_IN_FACE);
}
DiamondStorage storage ds = diamondStorage();
if (LibUtil.isZeroAddress(_facetAddress)) {
revert DiamondError(FACET_ADDRESS_IS_ZERO);
}
uint96 selectorPosition = uint96(ds.facetFunctionSelectors[_facetAddress].functionSelectors.length);
// add new facet address if it does not exist
if (selectorPosition == 0) {
addFacet(ds, _facetAddress);
}
for (uint256 selectorIndex; selectorIndex < _functionSelectors.length; ) {
bytes4 selector = _functionSelectors[selectorIndex];
address oldFacetAddress = ds.selectorToFacetAndPosition[selector].facetAddress;
if (!LibUtil.isZeroAddress(oldFacetAddress)) {
revert DiamondError(FUNCTION_ALREADY_EXISTS);
}
addFunction(ds, selector, selectorPosition, _facetAddress);
unchecked {
++selectorPosition;
++selectorIndex;
}
}
}
function replaceFunctions(address _facetAddress, bytes4[] memory _functionSelectors) internal {
if (_functionSelectors.length == 0) {
revert DiamondError(NO_SELECTORS_IN_FACE);
}
DiamondStorage storage ds = diamondStorage();
if (LibUtil.isZeroAddress(_facetAddress)) {
revert DiamondError(FACET_ADDRESS_IS_ZERO);
}
uint96 selectorPosition = uint96(ds.facetFunctionSelectors[_facetAddress].functionSelectors.length);
// add new facet address if it does not exist
if (selectorPosition == 0) {
addFacet(ds, _facetAddress);
}
for (uint256 selectorIndex; selectorIndex < _functionSelectors.length; ) {
bytes4 selector = _functionSelectors[selectorIndex];
address oldFacetAddress = ds.selectorToFacetAndPosition[selector].facetAddress;
if (oldFacetAddress == _facetAddress) {
revert DiamondError(FUNCTION_ALREADY_EXISTS);
}
removeFunction(ds, oldFacetAddress, selector);
addFunction(ds, selector, selectorPosition, _facetAddress);
unchecked {
++selectorPosition;
++selectorIndex;
}
}
}
function removeFunctions(address _facetAddress, bytes4[] memory _functionSelectors) internal {
if (_functionSelectors.length == 0) {
revert DiamondError(NO_SELECTORS_IN_FACE);
}
DiamondStorage storage ds = diamondStorage();
// if function does not exist then do nothing and return
if (!LibUtil.isZeroAddress(_facetAddress)) {
revert DiamondError(FACET_ADDRESS_IS_NOT_ZERO);
}
for (uint256 selectorIndex; selectorIndex < _functionSelectors.length; ) {
bytes4 selector = _functionSelectors[selectorIndex];
address oldFacetAddress = ds.selectorToFacetAndPosition[selector].facetAddress;
removeFunction(ds, oldFacetAddress, selector);
unchecked {
++selectorIndex;
}
}
}
function addFacet(DiamondStorage storage ds, address _facetAddress) internal {
enforceHasContractCode(_facetAddress);
ds.facetFunctionSelectors[_facetAddress].facetAddressPosition = ds.facetAddresses.length;
ds.facetAddresses.push(_facetAddress);
}
function addFunction(
DiamondStorage storage ds,
bytes4 _selector,
uint96 _selectorPosition,
address _facetAddress
) internal {
ds.selectorToFacetAndPosition[_selector].functionSelectorPosition = _selectorPosition;
ds.facetFunctionSelectors[_facetAddress].functionSelectors.push(_selector);
ds.selectorToFacetAndPosition[_selector].facetAddress = _facetAddress;
}
function removeFunction(DiamondStorage storage ds, address _facetAddress, bytes4 _selector) internal {
if (LibUtil.isZeroAddress(_facetAddress)) {
revert DiamondError(FUNCTION_DOES_NOT_EXIST);
}
// an immutable function is a function defined directly in a diamond
if (_facetAddress == address(this)) {
revert DiamondError(FUNCTION_IS_IMMUTABLE);
}
// replace selector with last selector, then delete last selector
uint256 selectorPosition = ds.selectorToFacetAndPosition[_selector].functionSelectorPosition;
uint256 lastSelectorPosition = ds.facetFunctionSelectors[_facetAddress].functionSelectors.length - 1;
// if not the same then replace _selector with lastSelector
if (selectorPosition != lastSelectorPosition) {
bytes4 lastSelector = ds.facetFunctionSelectors[_facetAddress].functionSelectors[lastSelectorPosition];
ds.facetFunctionSelectors[_facetAddress].functionSelectors[selectorPosition] = lastSelector;
ds.selectorToFacetAndPosition[lastSelector].functionSelectorPosition = uint96(selectorPosition);
}
// delete the last selector
ds.facetFunctionSelectors[_facetAddress].functionSelectors.pop();
delete ds.selectorToFacetAndPosition[_selector];
// if no more selectors for facet address then delete the facet address
if (lastSelectorPosition == 0) {
// replace facet address with last facet address and delete last facet address
uint256 lastFacetAddressPosition = ds.facetAddresses.length - 1;
uint256 facetAddressPosition = ds.facetFunctionSelectors[_facetAddress].facetAddressPosition;
if (facetAddressPosition != lastFacetAddressPosition) {
address lastFacetAddress = ds.facetAddresses[lastFacetAddressPosition];
ds.facetAddresses[facetAddressPosition] = lastFacetAddress;
ds.facetFunctionSelectors[lastFacetAddress].facetAddressPosition = facetAddressPosition;
}
ds.facetAddresses.pop();
delete ds.facetFunctionSelectors[_facetAddress].facetAddressPosition;
}
}
function initializeDiamondCut(address _init, bytes memory _calldata) internal {
if (LibUtil.isZeroAddress(_init)) {
if (_calldata.length != 0) {
revert DiamondError(INIT_ZERO_BUT_CALLDATA_NOT_EMPTY);
}
} else {
if (_calldata.length == 0) {
revert DiamondError(CALLDATA_EMPTY_BUT_INIT_NOT_ZERO);
}
if (_init != address(this)) {
enforceHasContractCode(_init);
}
// solhint-disable-next-line avoid-low-level-calls
(bool success, bytes memory error) = _init.delegatecall(_calldata);
if (!success) {
if (error.length > 0) {
// bubble up error
/// @solidity memory-safe-assembly
assembly ("memory-safe") {
let returndata_size := mload(error)
revert(add(32, error), returndata_size)
}
} else {
revert DiamondError(INIT_REVERTED);
}
}
}
}
function enforceHasContractCode(address _contract) internal view {
uint256 contractSize;
// solhint-disable-next-line no-inline-assembly
assembly ("memory-safe") {
contractSize := extcodesize(_contract)
}
if (contractSize == 0) {
revert DiamondError(FACET_CONTAINS_NO_CODE);
}
}
}// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /******************************************************************************\ * Author: Nick Mudge <[email protected]> (https://twitter.com/mudgen) * EIP-2535 Diamonds: https://eips.ethereum.org/EIPS/eip-2535 /******************************************************************************/ interface IDiamondCut { enum FacetCutAction { Add, Replace, Remove } // Add=0, Replace=1, Remove=2 struct FacetCut { address facetAddress; FacetCutAction action; bytes4[] functionSelectors; } /// @notice Add/replace/remove any number of functions and optionally execute /// a function with delegatecall /// @param _diamondCut Contains the facet addresses and function selectors /// @param _init The address of the contract or facet to execute _calldata /// @param _calldata A function call, including function selector and arguments /// _calldata is executed with delegatecall on _init function diamondCut(FacetCut[] calldata _diamondCut, address _init, bytes calldata _calldata) external; event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata); }
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
import "./LibBytes.sol";
library LibUtil {
using LibBytes for bytes;
function getRevertMsg(bytes memory _res) internal pure returns (string memory) {
// If the _res length is less than 68, then the transaction failed silently (without a revert message)
if (_res.length < 68) return "Transaction reverted silently";
bytes memory revertData = _res.slice(4, _res.length - 4); // Remove the selector which is the first 4 bytes
return abi.decode(revertData, (string)); // All that remains is the revert string
}
/// @notice Determines whether the given address is the zero address
/// @param addr The address to verify
/// @return Boolean indicating if the address is the zero address
function isZeroAddress(address addr) internal pure returns (bool) {
return addr == address(0);
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
import {GenericErrors} from "contracts/Errors/GenericErrors.sol";
import {SafeCast160} from "permit2/src/libraries/SafeCast160.sol";
import {SafeTransferLib} from "contracts/Libraries/SafeTransferLib.sol";
import {Constants} from "contracts/Libraries/Constants.sol";
import {ERC20} from "solmate/tokens/ERC20.sol";
import {ERC721} from "solmate/tokens/ERC721.sol";
import {ERC1155} from "solmate/tokens/ERC1155.sol";
import {IWETH9} from "contracts/Interfaces/IWETH9.sol";
import {IAllowanceTransfer} from "permit2/src/interfaces/IAllowanceTransfer.sol";
import {UniERC20} from "contracts/Libraries/UniERC20.sol";
type Recipient is address;
type Payer is address;
/// @title Payments Library
/// @author Eisen (https://app.eisenfinance.com)
/// @notice Provides functionality for transferring assets
library LibPayments {
using SafeTransferLib for address;
using SafeCast160 for uint256;
using UniERC20 for address;
/// Types ///
bytes32 internal constant NAMESPACE = bytes32(0x6a2b33915c87ebbd2e7a47520fe4aaa6f0e18ef6bdebd64915d7aeced08d447e);
// bytes32(uint256(bytes32(keccak256("com.eisen.library.payments")))-1);
uint256 internal constant FEE_BIPS_BASE = 10_000;
/// Storage ///
struct PaymentStorage {
uint256 fee;
address feeCollector;
address WETH9;
address PERMIT2;
bool initialized;
}
/// Events ///
event FeeChanged(uint256 feeOld, uint256 feeNew);
event FeeCollectorChanged(address feeCollectorOld, address feeCollectorNew);
event PaymentInitialized(address indexed weth9, address indexed permit2, address indexed feeCollector, uint256 fee);
/// @dev Fetch local storage
function paymentStorage() internal pure returns (PaymentStorage storage payStor) {
bytes32 position = NAMESPACE;
// solhint-disable-next-line no-inline-assembly
assembly ("memory-safe") {
payStor.slot := position
}
}
/// @notice Set fee bips
/// @param fee fee portion in bips
function setFee(uint256 fee) internal {
PaymentStorage storage payStor = paymentStorage();
if (fee > FEE_BIPS_BASE / 10) {
revert GenericErrors.GenericError(GenericErrors.INVALID_FEE_AMOUNT);
}
emit FeeChanged(payStor.fee, fee);
payStor.fee = fee;
}
/// @notice Set fee collector
/// @param feeCollector The address of feeCollector
function changeFeeCollector(address feeCollector) internal {
PaymentStorage storage payStor = paymentStorage();
emit FeeCollectorChanged(payStor.feeCollector, feeCollector);
payStor.feeCollector = feeCollector;
}
/// @notice Initializes parameters for transferring assets
/// @param weth9 The address of wrapped native token
/// @param permit2 The address of permit2 contract
function initializePayment(address weth9, address permit2, address feeCollector, uint256 fee) internal {
PaymentStorage storage payStor = paymentStorage();
if (!payStor.initialized) {
payStor.WETH9 = weth9;
payStor.PERMIT2 = permit2;
payStor.feeCollector = feeCollector;
payStor.fee = fee;
payStor.initialized = true;
} else {
revert GenericErrors.GenericError(GenericErrors.ALREADY_INITIALIZED);
}
emit PaymentInitialized(weth9, permit2, feeCollector, fee);
}
/// @notice Gets the address of wrapped native token
/// @return The address of wrapped native token
function WETH() internal view returns (address) {
return paymentStorage().WETH9;
}
/// @notice Calculates the recipient address for a command
/// @param recipient The recipient or recipient-flag for the command
/// @return outRecipient The resultant recipient for the command
function map(Recipient recipient) internal view returns (Recipient outRecipient) {
if (Recipient.unwrap(recipient) == Constants.MSG_SENDER) {
outRecipient = Recipient.wrap(msg.sender);
} else if (Recipient.unwrap(recipient) == Constants.ADDRESS_THIS) {
outRecipient = Recipient.wrap(address(this));
} else if (UniERC20.isETH(Recipient.unwrap(recipient))) {
// ETH is a special case, it is not a valid recipient(address(0))
outRecipient = Recipient.wrap(msg.sender);
} else {
outRecipient = recipient;
}
}
/// @notice Calculates the payer address for a command
/// @param payer The payer-flag for the command
/// @return outPayer The resultant payer for the command
function payerMap(Payer payer) internal view returns (Payer outPayer) {
if (Payer.unwrap(payer) == Constants.MSG_SENDER) {
outPayer = Payer.wrap(msg.sender);
} else if (Payer.unwrap(payer) == Constants.ADDRESS_THIS) {
outPayer = Payer.wrap(address(this));
} else {
revert GenericErrors.GenericError(GenericErrors.INVALID_PARAMS);
}
}
/// @notice Pays an amount of ETH or ERC20 to a recipient
/// @param token The token to pay (can be ETH using Constants.ETH)
/// @param recipient The Recipient that will receive the payment
/// @param value The amount to pay
function pay(address token, Recipient recipient, uint256 value) internal {
if (Recipient.unwrap(recipient) == address(this)) return;
if (token.isETH()) {
Recipient.unwrap(recipient).safeTransferETH(value);
} else {
if (value == Constants.CONTRACT_BALANCE) {
value = token.balanceOf(address(this));
}
token.safeTransfer(Recipient.unwrap(recipient), value);
}
}
/// @notice Pays a proportion of the contract's ETH or ERC20 to a recipient
/// @param token The token to pay (can be ETH using Constants.ETH)
/// @param recipient The Recipient that will receive payment
/// @param bips Portion in bips of whole balance of the contract
function payPortion(address token, Recipient recipient, uint256 bips) internal {
if (bips == 0 || bips > 10_000) revert GenericErrors.GenericError(GenericErrors.INVALID_BIPS);
if (token.isETH()) {
uint256 balance = address(this).balance;
uint256 amount = (balance * bips) / FEE_BIPS_BASE;
Recipient.unwrap(recipient).safeTransferETH(amount);
} else {
uint256 balance = ERC20(token).balanceOf(address(this));
uint256 amount = (balance * bips) / FEE_BIPS_BASE;
// pay with tokens already in the contract (for the exact input multihop case)
token.safeTransfer(Recipient.unwrap(recipient), amount);
}
}
/// @notice Sweeps all of the contract's ERC20 or ETH to an address
/// @param token The token to sweep (can be ETH using Constants.ETH)
/// @param recipient The address that will receive payment
/// @param amountMinimum The minimum desired amount
function sweep(address token, Recipient recipient, uint256 amountMinimum) internal {
uint256 balance;
if (token.isETH()) {
balance = address(this).balance;
if (balance < amountMinimum) revert GenericErrors.GenericError(GenericErrors.INSUFFICIENT_VALUE);
if (balance > 0) Recipient.unwrap(recipient).safeTransferETH(balance);
} else {
balance = token.balanceOf(address(this));
if (balance < amountMinimum) revert GenericErrors.GenericError(GenericErrors.INSUFFICIENT_TOKEN);
if (balance > 0) token.safeTransfer(Recipient.unwrap(recipient), balance);
}
}
/// @notice Sweeps an ERC721 to a recipient from the contract
/// @param token The ERC721 token to sweep
/// @param recipient The address that will receive payment
/// @param id The ID of the ERC721 to sweep
function sweepERC721(address token, Recipient recipient, uint256 id) internal {
ERC721(token).safeTransferFrom(address(this), Recipient.unwrap(recipient), id);
}
/// @notice Sweeps all of the contract's ERC1155 to an address
/// @param token The ERC1155 token to sweep
/// @param recipient The address that will receive payment
/// @param id The ID of the ERC1155 to sweep
/// @param amountMinimum The minimum desired amount
function sweepERC1155(address token, Recipient recipient, uint256 id, uint256 amountMinimum) internal {
uint256 balance = ERC1155(token).balanceOf(address(this), id);
if (balance < amountMinimum) revert GenericErrors.GenericError(GenericErrors.INSUFFICIENT_TOKEN);
ERC1155(token).safeTransferFrom(address(this), Recipient.unwrap(recipient), id, balance, bytes(""));
}
/// @notice Wraps an amount of ETH into WETH
/// @param recipient The recipient of the WETH
/// @param amount The amount to wrap (can be CONTRACT_BALANCE)
function wrapETH(Recipient recipient, uint256 amount) internal returns (uint256 amountOut) {
if (amount == Constants.CONTRACT_BALANCE) {
amount = address(this).balance;
} else if (amount > address(this).balance) {
revert GenericErrors.GenericError(GenericErrors.INSUFFICIENT_VALUE);
}
if (amount > 0) {
PaymentStorage storage ps = paymentStorage();
IWETH9(ps.WETH9).deposit{value: amount}();
if (Recipient.unwrap(recipient) != address(this)) {
IWETH9(ps.WETH9).transfer(Recipient.unwrap(recipient), amount);
}
}
amountOut = amount;
}
/// @notice Unwraps the amount of the contract's WETH into ETH
/// @param recipient The recipient of the ETH
/// @param amount The minimum amount of ETH desired
function unwrapWETH9(Recipient recipient, uint256 amount) internal returns (uint256 amountOut) {
PaymentStorage storage ps = paymentStorage();
if (IWETH9(ps.WETH9).balanceOf(address(this)) < amount) {
revert GenericErrors.GenericError(GenericErrors.INSUFFICIENT_VALUE);
}
IWETH9(ps.WETH9).withdraw(amount);
if (Recipient.unwrap(recipient) != address(this)) {
Recipient.unwrap(recipient).safeTransferETH(amount);
}
amountOut = amount;
}
/// @notice Performs a approve function on Permit2
/// @param token The token address
/// @param spender The spender address
function approveMax(address token, address spender, uint256 amount) internal {
PaymentStorage storage ps = paymentStorage();
(uint256 allowance, , ) = IAllowanceTransfer(ps.PERMIT2).allowance(address(this), token, spender);
if (allowance < amount) {
IAllowanceTransfer(ps.PERMIT2).approve(token, spender, type(uint160).max, type(uint48).max);
}
}
/// @notice Performs a approve function on Permit2
/// @param token The token address
/// @param spender The spender address
function approveWithOutExpiration(address token, address spender, uint256 amount) internal {
PaymentStorage storage ps = paymentStorage();
IAllowanceTransfer(ps.PERMIT2).approve(token, spender, amount.toUint160(), type(uint48).max);
}
/// @notice Performs a permit function on Permit2
/// @param owner The token owner address
/// @param permitSingle A single of permit description
/// @param signature A single of permit data with signature
function permit(
address owner,
IAllowanceTransfer.PermitSingle memory permitSingle,
bytes memory signature
) internal {
PaymentStorage storage ps = paymentStorage();
IAllowanceTransfer(ps.PERMIT2).permit(owner, permitSingle, signature);
}
/// @notice Performs a batch permit function on Permit2
/// @param owner The token owner address
/// @param permitBatch A batch of permit descriptions
/// @param signature A batch of permit data with signature
function permit(address owner, IAllowanceTransfer.PermitBatch memory permitBatch, bytes memory signature) internal {
PaymentStorage storage ps = paymentStorage();
IAllowanceTransfer(ps.PERMIT2).permit(owner, permitBatch, signature);
}
/// @notice Performs a transferFrom on Permit2
/// @param token The token to transfer
/// @param from The address to transfer from
/// @param to The recipient of the transfer
/// @param amount The amount to transfer
function permit2TransferFrom(address token, address from, address to, uint160 amount) internal {
_permit2TransferFrom(token, from, to, amount);
}
/// @notice Performs a batch transferFrom on Permit2
/// @param batchDetails An array detailing each of the transfers that should occur
function permit2TransferFrom(IAllowanceTransfer.AllowanceTransferDetails[] memory batchDetails) internal {
address owner = msg.sender;
uint256 batchLength = batchDetails.length;
PaymentStorage storage ps = paymentStorage();
for (uint256 i = 0; i < batchLength; ++i) {
if (batchDetails[i].from != owner) revert GenericErrors.GenericError(GenericErrors.FROM_ADDR_IS_NOT_OWNER);
}
for (uint256 i = 0; i < batchLength; ++i) {
_permit2TransferFrom(
batchDetails[i].token,
batchDetails[i].from,
batchDetails[i].to,
batchDetails[i].amount
);
}
}
/// @notice Either performs a regular payment or transferFrom on Permit2, depending on the payer address
/// @param token The token to transfer
/// @param payer The address to pay for the transfer
/// @param recipient The recipient of the transfer
/// @param amount The amount to transfer
function payOrPermit2Transfer(address token, Payer payer, Recipient recipient, uint256 amount) internal {
if (Payer.unwrap(payer) == address(this)) pay(token, recipient, amount);
else payFrom(token, payer, recipient, amount.toUint160());
}
/// @notice Performs a transferFrom on Permit2
/// @param token The token to transfer
/// @param payer The address to pay for the transfer
/// @param recipient The recipient of the transfer
/// @param amount The amount to transfer
function payFrom(address token, Payer payer, Recipient recipient, uint256 amount) internal {
if (Payer.unwrap(payer) == Recipient.unwrap(recipient)) return;
if (!token.isETH()) {
_permit2TransferFrom(token, Payer.unwrap(payer), Recipient.unwrap(recipient), amount.toUint160());
}
}
/// @notice Performs a transferFrom on Permit2 internally
/// @param token The token to transfer
/// @param from The address to transfer from
/// @param to The recipient of the transfer
/// @param amount The amount to transfer
function _permit2TransferFrom(address token, address from, address to, uint160 amount) internal {
if (from == to) return;
PaymentStorage storage ps = paymentStorage();
IAllowanceTransfer(ps.PERMIT2).transferFrom(from, to, amount, token);
}
}// 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 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.
contract TransferrableOwnership {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* 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();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* 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: `not(_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.
uint256 private constant _OWNER_SLOT_NOT = 0x8b78c6d8;
/// 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;
constructor(address newOwner) {
if (newOwner != address(0)) {
_initializeOwner(newOwner);
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* INTERNAL FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @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 {
/// @solidity memory-safe-assembly
assembly ("memory-safe") {
// Clean the upper 96 bits.
newOwner := shr(96, shl(96, newOwner))
// Store the new value.
sstore(not(_OWNER_SLOT_NOT), 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 {
/// @solidity memory-safe-assembly
assembly ("memory-safe") {
let ownerSlot := not(_OWNER_SLOT_NOT)
// 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 ("memory-safe") {
// If the caller is not the stored owner, revert.
if iszero(eq(caller(), sload(not(_OWNER_SLOT_NOT)))) {
mstore(0x00, 0x82b42900) // `Unauthorized()`.
revert(0x1c, 0x04)
}
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* 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 ("memory-safe") {
if iszero(shl(96, newOwner)) {
mstore(0x00, 0x7448fbae) // `NewOwnerIsZeroAddress()`.
revert(0x1c, 0x04)
}
}
_setOwner(newOwner);
}
/// @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 ("memory-safe") {
// 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 cancelOwnershipTransfer() public payable virtual {
/// @solidity memory-safe-assembly
assembly ("memory-safe") {
// 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 confirmOwnershipTransfer(address pendingOwner) public payable virtual onlyOwner {
/// @solidity memory-safe-assembly
assembly ("memory-safe") {
// 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 ("memory-safe") {
result := sload(not(_OWNER_SLOT_NOT))
}
}
/// @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 ("memory-safe") {
// Compute the handover slot.
mstore(0x0c, _HANDOVER_SLOT_SEED)
mstore(0x00, pendingOwner)
// Load the handover slot.
result := sload(keccak256(0x0c, 0x20))
}
}
/// @dev Returns how long a two-step ownership handover is valid for in seconds.
function ownershipHandoverValidFor() public view virtual returns (uint64) {
return 48 * 3600;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* MODIFIERS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Marks a function as only callable by the owner.
modifier onlyOwner() virtual {
_checkOwner();
_;
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
library GenericErrors {
error DiffError(uint256 errorCode, uint256 stdValue, uint256 realValue); // 0dd10eb1
error GenericError(uint256 errorCode); // 948ac503
uint256 public constant ALREADY_INITIALIZED = 1;
uint256 public constant CANNOT_AUTHORIZE_SELF = 2;
uint256 public constant CANNOT_BRIDGE_TO_SAME_NETWORK = 3;
uint256 public constant CONTRACT_CALL_NOT_ALLOWED = 4;
uint256 public constant WITHDRAW_FAILED = 5;
uint256 public constant EXTERNAL_CALL_FAILED = 6;
uint256 public constant INFORMATION_MISMATCH = 7;
uint256 public constant ZERO_AMOUNT = 8;
uint256 public constant INVALID_AMOUNT = 9;
uint256 public constant INSUFFICIENT_VALUE = 10;
uint256 public constant INSUFFICIENT_TOKEN = 11;
uint256 public constant INSUFFICIENT_BALANCE = 12;
uint256 public constant INVALID_DESTINATION_CHAIN = 13;
uint256 public constant INVALID_FALLBACK_ADDRESS = 14;
uint256 public constant INVALID_RECEIVER = 15;
uint256 public constant INVALID_SENDING_TOKEN = 16;
uint256 public constant NATIVE_ASSET_NOT_SUPPORTED = 17;
uint256 public constant NATIVE_ASSET_TRANSFER_FAILED = 18;
uint256 public constant NO_SWAP_DATA_PROVIDED = 19;
uint256 public constant NO_SWAP_FROM_ZERO_BALANCE = 20;
uint256 public constant NOT_A_CONTRACT = 21;
uint256 public constant NOT_INITIALIZED = 22;
uint256 public constant NO_TRANSFER_TO_NULL_ADDRESS = 23;
uint256 public constant NULL_ADDR_IS_NOT_AN_ERC20_TOKEN = 24;
uint256 public constant NULL_ADDR_IS_NOT_A_VALID_SPENDER = 25;
uint256 public constant ONLY_CONTRACT_OWNER = 26;
uint256 public constant RECOVERY_ADDRESS_CANNOT_BE_ZERO = 27;
uint256 public constant REENTRANCY_ERROR = 28;
uint256 public constant TOKEN_NOT_SUPPORTED = 29;
uint256 public constant REENTRANCY_LOCKED = 30;
uint256 public constant UNSUPPORTED_CHAIN_ID = 31;
uint256 public constant UN_AUTHORIZED = 32;
uint256 public constant INVALID_CALL_DATA = 33;
uint256 public constant INVALID_CONFIG = 34;
uint256 public constant INVALID_CALLER = 35;
uint256 public constant INVALID_CONTRACT = 36;
uint256 public constant INVALID_BIPS = 37;
uint256 public constant INVALID_PARAMS = 38;
uint256 public constant INVALID_INITIATOR = 39;
uint256 public constant INVALID_POOL = 40;
uint256 public constant INVALID_TOKEN = 41;
uint256 public constant INVALID_INPUT_TOKEN = 42;
uint256 public constant INVALID_PATH = 43;
uint256 public constant INVALID_BATCH_PATH = 44;
uint256 public constant INVALID_FEE_AMOUNT = 45;
uint256 public constant DEADLINE = 46;
uint256 public constant LENGTH_MISMATCH = 47;
uint256 public constant OVERFLOW_UNDERFLOW = 48;
uint256 public constant FROM_ADDR_IS_NOT_OWNER = 49;
uint256 public constant UNI3_SLICE_OUT_OF_BOUNDS = 50;
uint256 public constant UNI3_SLICE_OVERFLOW = 51;
uint256 public constant UNI3_NO_SLICE = 52;
uint256 public constant UNI3_TO_UINT24_OUT_OF_BOUNDS = 53;
uint256 public constant UNI3_TO_UINT24_OVERFLOW = 54;
uint256 public constant UNI3_TO_ADDRESS_OVERFLOW = 55;
uint256 public constant UNI3_TO_ADDRESS_OUT_OF_BOUNDS = 56;
uint256 public constant MAVERICK_TO_UINT256_OVERFLOW = 60;
uint256 public constant MAVERICK_TO_UINT256_OUT_OF_BOUNDS = 61;
uint256 public constant MAVERICK_TO_INT256_OVERFLOW = 62;
uint256 public constant MAVERICK_TO_INT256_OUT_OF_BOUNDS = 63;
/// Pendle ///
uint256 public constant PENDLE_INSUFFICIENT_PT_REPAY = 71;
uint256 public constant PENDLE_INSUFFICIENT_SY_REPAY = 72;
uint256 public constant PENDLE_MARKET_EXPIRED = 73;
uint256 public constant PENDLE_INSUFFICIENT_PT_FOR_TRADE = 74;
uint256 public constant PENDLE_MARKET_ZERO_TOTAL_PT_OR_TOTAL_ASSET = 75;
uint256 public constant PENDLE_MARKET_ZERO_LN_IMPLILED_RATE = 76;
uint256 public constant PENDLE_MARKET_EXCHANGE_RATE_BELOW_ONE = 77;
uint256 public constant PENDLE_MARKET_RATE_SCALAR_BELOW_ZERO = 78;
uint256 public constant PENDLE_MARKET_PROPORTION_TOO_HIGH = 79;
uint256 public constant PENDLE_MARKET_PROPORTION_MUST_NOT_EQUAL_ONE = 710;
/// Dodo ///
uint256 public constant DODO_NO_SLICE = 80;
uint256 public constant DODO_SLICE_OUT_OF_BOUNDS = 81;
uint256 public constant DODO_SLICE_OVERFLOW = 82;
uint256 public constant DODO_TO_UINT8_OUT_OF_BOUNDS = 83;
uint256 public constant DODO_TO_UINT8_OVERFLOW = 84;
uint256 public constant DODO_TO_UINT256_OUT_OF_BOUNDS = 85;
uint256 public constant DODO_TO_UINT256_OVERFLOW = 86;
uint256 public constant DODO_TO_ADDRESS_OUT_OF_BOUNDS = 87;
uint256 public constant DODO_TO_ADDRESS_OVERFLOW = 88;
uint256 public constant SLIPPAGE = 101;
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
library LibBytes {
// solhint-disable no-inline-assembly
// LibBytes specific errors
error SliceOverflow();
error SliceOutOfBounds();
error AddressOutOfBounds();
bytes16 private constant _SYMBOLS = "0123456789abcdef";
// -------------------------
function slice(bytes memory _bytes, uint256 _start, uint256 _length) internal pure returns (bytes memory) {
if (_length + 31 < _length) revert SliceOverflow();
if (_bytes.length < _start + _length) revert SliceOutOfBounds();
bytes memory tempBytes;
assembly ("memory-safe") {
switch iszero(_length)
case 0 {
// Get a location of some free memory and store it in tempBytes as
// Solidity does for memory variables.
tempBytes := mload(0x40)
// The first word of the slice result is potentially a partial
// word read from the original array. To read it, we calculate
// the length of that partial word and start copying that many
// bytes into the array. The first word we copy will start with
// data we don't care about, but the last `lengthmod` bytes will
// land at the beginning of the contents of the new array. When
// we're done copying, we overwrite the full first word with
// the actual length of the slice.
let lengthmod := and(_length, 31)
// The multiplication in the next line is necessary
// because when slicing multiples of 32 bytes (lengthmod == 0)
// the following copy loop was copying the origin's length
// and then ending prematurely not copying everything it should.
let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod)))
let end := add(mc, _length)
for {
// The multiplication in the next line has the same exact purpose
// as the one above.
let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start)
} lt(mc, end) {
mc := add(mc, 0x20)
cc := add(cc, 0x20)
} {
mstore(mc, mload(cc))
}
mstore(tempBytes, _length)
//update free-memory pointer
//allocating the array padded to 32 bytes like the compiler does now
mstore(0x40, and(add(mc, 31), not(31)))
}
//if we want a zero-length slice let's just return a zero-length array
default {
tempBytes := mload(0x40)
//zero out the 32 bytes slice we are about to return
//we need to do it because Solidity does not garbage collect
mstore(tempBytes, 0)
mstore(0x40, add(tempBytes, 0x20))
}
}
return tempBytes;
}
function toAddress(bytes memory _bytes, uint256 _start) internal pure returns (address) {
if (_bytes.length < _start + 20) {
revert AddressOutOfBounds();
}
address tempAddress;
assembly ("memory-safe") {
tempAddress := div(mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000)
}
return tempAddress;
}
/// Copied from OpenZeppelin's `Strings.sol` utility library.
/// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/8335676b0e99944eef6a742e16dcd9ff6e68e609/contracts/utils/Strings.sol
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = _SYMBOLS[value & 0xf];
value >>= 4;
}
require(value == 0, "Strings: hex length insufficient");
return string(buffer);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
library SafeCast160 {
/// @notice Thrown when a valude greater than type(uint160).max is cast to uint160
error UnsafeCast();
/// @notice Safely casts uint256 to uint160
/// @param value The uint256 to be cast
function toUint160(uint256 value) internal pure returns (uint160) {
if (value > type(uint160).max) revert UnsafeCast();
return uint160(value);
}
}// 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)
///
/// @dev Note:
/// - For ETH transfers, please use `forceSafeTransferETH` for DoS protection.
/// - For ERC20s, this implementation won't check that a token has code,
/// responsibility is delegated to the caller.
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();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* 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;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* 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, gas(), 0x00, gas(), 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(), gas(), 0x00, gas(), 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, gas(), 0x00, gas(), 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)) {
returndatacopy(gas(), returndatasize(), shr(20, gas())) // 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(), gas(), 0x00, gas(), 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)) {
returndatacopy(gas(), returndatasize(), shr(20, gas())) // 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, gas(), 0x00, gas(), 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)) {
returndatacopy(gas(), returndatasize(), shr(20, gas())) // 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 {
if iszero(call(GAS_STIPEND_NO_GRIEF, to, selfbalance(), gas(), 0x00, gas(), 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)) {
returndatacopy(gas(), returndatasize(), shr(20, gas())) // 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, gas(), 0x00, gas(), 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(), gas(), 0x00, gas(), 0x00)
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ERC20 OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Sends `amount` of ERC20 `token` from `from` to `to`.
/// Reverts upon failure.
///
/// The `from` account must have at least `amount` approved for
/// the current contract to manage.
function safeTransferFrom(address token, address from, address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x60, amount) // Store the `amount` argument.
mstore(0x40, to) // Store the `to` argument.
mstore(0x2c, shl(96, from)) // Store the `from` argument.
mstore(0x0c, 0x23b872dd000000000000000000000000) // `transferFrom(address,address,uint256)`.
// Perform the transfer, reverting upon failure.
if iszero(
and(
// The arguments of `and` are evaluated from right to left.
or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
)
) {
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 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.
if iszero(
and(
// The arguments of `and` are evaluated from right to left.
or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
)
) {
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.
if iszero(
and(
// The arguments of `and` are evaluated from right to left.
or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
)
) {
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.
if iszero(
and(
// The arguments of `and` are evaluated from right to left.
or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
)
) {
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)`.
// Perform the approval, reverting upon failure.
if iszero(
and(
// The arguments of `and` are evaluated from right to left.
or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
)
) {
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.
if iszero(
and(
// The arguments of `and` are evaluated from right to left.
or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
)
) {
mstore(0x34, 0) // Store 0 for the `amount`.
mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
pop(call(gas(), token, 0, 0x10, 0x44, 0x00, 0x00)) // Reset the approval.
mstore(0x34, amount) // Store back the original `amount`.
// Retry the approval, reverting upon failure.
if iszero(
and(
or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
)
) {
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(
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)
)
)
}
}
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.17;
/// @title Constant state
/// @notice Constant state used by the Universal Router
library Constants {
/// @dev Used for identifying cases when this contract's balance of a token is to be used as an input
/// This value is equivalent to 1<<255, i.e. a singular 1 in the most significant bit.
uint256 internal constant CONTRACT_BALANCE = 0x8000000000000000000000000000000000000000000000000000000000000000;
/// @dev Used for identifying cases when a v2 pair has already received input tokens
uint256 internal constant ALREADY_PAID = 0;
/// @dev Used as a flag for identifying the transfer of ETH instead of a token
address internal constant ETH = address(0);
/// @dev Used for ETH address
address internal constant ETH_ADDRESS = address(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE);
/// @dev Used as a flag for identifying that msg.sender should be used, saves gas by sending more 0 bytes
address internal constant MSG_SENDER = address(1);
/// @dev Used as a flag for identifying address(this) should be used, saves gas by sending more 0 bytes
address internal constant ADDRESS_THIS = address(2);
}// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)
/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)
/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.
abstract contract ERC20 {
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
event Transfer(address indexed from, address indexed to, uint256 amount);
event Approval(address indexed owner, address indexed spender, uint256 amount);
/*//////////////////////////////////////////////////////////////
METADATA STORAGE
//////////////////////////////////////////////////////////////*/
string public name;
string public symbol;
uint8 public immutable decimals;
/*//////////////////////////////////////////////////////////////
ERC20 STORAGE
//////////////////////////////////////////////////////////////*/
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;
/*//////////////////////////////////////////////////////////////
EIP-2612 STORAGE
//////////////////////////////////////////////////////////////*/
uint256 internal immutable INITIAL_CHAIN_ID;
bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;
mapping(address => uint256) public nonces;
/*//////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/
constructor(
string memory _name,
string memory _symbol,
uint8 _decimals
) {
name = _name;
symbol = _symbol;
decimals = _decimals;
INITIAL_CHAIN_ID = block.chainid;
INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
}
/*//////////////////////////////////////////////////////////////
ERC20 LOGIC
//////////////////////////////////////////////////////////////*/
function approve(address spender, uint256 amount) public virtual returns (bool) {
allowance[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
return true;
}
function transfer(address to, uint256 amount) public virtual returns (bool) {
balanceOf[msg.sender] -= amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(msg.sender, to, amount);
return true;
}
function transferFrom(
address from,
address to,
uint256 amount
) public virtual returns (bool) {
uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.
if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;
balanceOf[from] -= amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(from, to, amount);
return true;
}
/*//////////////////////////////////////////////////////////////
EIP-2612 LOGIC
//////////////////////////////////////////////////////////////*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) public virtual {
require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");
// Unchecked because the only math done is incrementing
// the owner's nonce which cannot realistically overflow.
unchecked {
address recoveredAddress = ecrecover(
keccak256(
abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR(),
keccak256(
abi.encode(
keccak256(
"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
),
owner,
spender,
value,
nonces[owner]++,
deadline
)
)
)
),
v,
r,
s
);
require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");
allowance[recoveredAddress][spender] = value;
}
emit Approval(owner, spender, value);
}
function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
}
function computeDomainSeparator() internal view virtual returns (bytes32) {
return
keccak256(
abi.encode(
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
keccak256(bytes(name)),
keccak256("1"),
block.chainid,
address(this)
)
);
}
/*//////////////////////////////////////////////////////////////
INTERNAL MINT/BURN LOGIC
//////////////////////////////////////////////////////////////*/
function _mint(address to, uint256 amount) internal virtual {
totalSupply += amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(address(0), to, amount);
}
function _burn(address from, uint256 amount) internal virtual {
balanceOf[from] -= amount;
// Cannot underflow because a user's balance
// will never be larger than the total supply.
unchecked {
totalSupply -= amount;
}
emit Transfer(from, address(0), amount);
}
}// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
/// @notice Modern, minimalist, and gas efficient ERC-721 implementation.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC721.sol)
abstract contract ERC721 {
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
event Transfer(address indexed from, address indexed to, uint256 indexed id);
event Approval(address indexed owner, address indexed spender, uint256 indexed id);
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
/*//////////////////////////////////////////////////////////////
METADATA STORAGE/LOGIC
//////////////////////////////////////////////////////////////*/
string public name;
string public symbol;
function tokenURI(uint256 id) public view virtual returns (string memory);
/*//////////////////////////////////////////////////////////////
ERC721 BALANCE/OWNER STORAGE
//////////////////////////////////////////////////////////////*/
mapping(uint256 => address) internal _ownerOf;
mapping(address => uint256) internal _balanceOf;
function ownerOf(uint256 id) public view virtual returns (address owner) {
require((owner = _ownerOf[id]) != address(0), "NOT_MINTED");
}
function balanceOf(address owner) public view virtual returns (uint256) {
require(owner != address(0), "ZERO_ADDRESS");
return _balanceOf[owner];
}
/*//////////////////////////////////////////////////////////////
ERC721 APPROVAL STORAGE
//////////////////////////////////////////////////////////////*/
mapping(uint256 => address) public getApproved;
mapping(address => mapping(address => bool)) public isApprovedForAll;
/*//////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/
constructor(string memory _name, string memory _symbol) {
name = _name;
symbol = _symbol;
}
/*//////////////////////////////////////////////////////////////
ERC721 LOGIC
//////////////////////////////////////////////////////////////*/
function approve(address spender, uint256 id) public virtual {
address owner = _ownerOf[id];
require(msg.sender == owner || isApprovedForAll[owner][msg.sender], "NOT_AUTHORIZED");
getApproved[id] = spender;
emit Approval(owner, spender, id);
}
function setApprovalForAll(address operator, bool approved) public virtual {
isApprovedForAll[msg.sender][operator] = approved;
emit ApprovalForAll(msg.sender, operator, approved);
}
function transferFrom(
address from,
address to,
uint256 id
) public virtual {
require(from == _ownerOf[id], "WRONG_FROM");
require(to != address(0), "INVALID_RECIPIENT");
require(
msg.sender == from || isApprovedForAll[from][msg.sender] || msg.sender == getApproved[id],
"NOT_AUTHORIZED"
);
// Underflow of the sender's balance is impossible because we check for
// ownership above and the recipient's balance can't realistically overflow.
unchecked {
_balanceOf[from]--;
_balanceOf[to]++;
}
_ownerOf[id] = to;
delete getApproved[id];
emit Transfer(from, to, id);
}
function safeTransferFrom(
address from,
address to,
uint256 id
) public virtual {
transferFrom(from, to, id);
require(
to.code.length == 0 ||
ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, "") ==
ERC721TokenReceiver.onERC721Received.selector,
"UNSAFE_RECIPIENT"
);
}
function safeTransferFrom(
address from,
address to,
uint256 id,
bytes calldata data
) public virtual {
transferFrom(from, to, id);
require(
to.code.length == 0 ||
ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, data) ==
ERC721TokenReceiver.onERC721Received.selector,
"UNSAFE_RECIPIENT"
);
}
/*//////////////////////////////////////////////////////////////
ERC165 LOGIC
//////////////////////////////////////////////////////////////*/
function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
return
interfaceId == 0x01ffc9a7 || // ERC165 Interface ID for ERC165
interfaceId == 0x80ac58cd || // ERC165 Interface ID for ERC721
interfaceId == 0x5b5e139f; // ERC165 Interface ID for ERC721Metadata
}
/*//////////////////////////////////////////////////////////////
INTERNAL MINT/BURN LOGIC
//////////////////////////////////////////////////////////////*/
function _mint(address to, uint256 id) internal virtual {
require(to != address(0), "INVALID_RECIPIENT");
require(_ownerOf[id] == address(0), "ALREADY_MINTED");
// Counter overflow is incredibly unrealistic.
unchecked {
_balanceOf[to]++;
}
_ownerOf[id] = to;
emit Transfer(address(0), to, id);
}
function _burn(uint256 id) internal virtual {
address owner = _ownerOf[id];
require(owner != address(0), "NOT_MINTED");
// Ownership check above ensures no underflow.
unchecked {
_balanceOf[owner]--;
}
delete _ownerOf[id];
delete getApproved[id];
emit Transfer(owner, address(0), id);
}
/*//////////////////////////////////////////////////////////////
INTERNAL SAFE MINT LOGIC
//////////////////////////////////////////////////////////////*/
function _safeMint(address to, uint256 id) internal virtual {
_mint(to, id);
require(
to.code.length == 0 ||
ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, "") ==
ERC721TokenReceiver.onERC721Received.selector,
"UNSAFE_RECIPIENT"
);
}
function _safeMint(
address to,
uint256 id,
bytes memory data
) internal virtual {
_mint(to, id);
require(
to.code.length == 0 ||
ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, data) ==
ERC721TokenReceiver.onERC721Received.selector,
"UNSAFE_RECIPIENT"
);
}
}
/// @notice A generic interface for a contract which properly accepts ERC721 tokens.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC721.sol)
abstract contract ERC721TokenReceiver {
function onERC721Received(
address,
address,
uint256,
bytes calldata
) external virtual returns (bytes4) {
return ERC721TokenReceiver.onERC721Received.selector;
}
}// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
/// @notice Minimalist and gas efficient standard ERC1155 implementation.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC1155.sol)
abstract contract ERC1155 {
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
event TransferSingle(
address indexed operator,
address indexed from,
address indexed to,
uint256 id,
uint256 amount
);
event TransferBatch(
address indexed operator,
address indexed from,
address indexed to,
uint256[] ids,
uint256[] amounts
);
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
event URI(string value, uint256 indexed id);
/*//////////////////////////////////////////////////////////////
ERC1155 STORAGE
//////////////////////////////////////////////////////////////*/
mapping(address => mapping(uint256 => uint256)) public balanceOf;
mapping(address => mapping(address => bool)) public isApprovedForAll;
/*//////////////////////////////////////////////////////////////
METADATA LOGIC
//////////////////////////////////////////////////////////////*/
function uri(uint256 id) public view virtual returns (string memory);
/*//////////////////////////////////////////////////////////////
ERC1155 LOGIC
//////////////////////////////////////////////////////////////*/
function setApprovalForAll(address operator, bool approved) public virtual {
isApprovedForAll[msg.sender][operator] = approved;
emit ApprovalForAll(msg.sender, operator, approved);
}
function safeTransferFrom(
address from,
address to,
uint256 id,
uint256 amount,
bytes calldata data
) public virtual {
require(msg.sender == from || isApprovedForAll[from][msg.sender], "NOT_AUTHORIZED");
balanceOf[from][id] -= amount;
balanceOf[to][id] += amount;
emit TransferSingle(msg.sender, from, to, id, amount);
require(
to.code.length == 0
? to != address(0)
: ERC1155TokenReceiver(to).onERC1155Received(msg.sender, from, id, amount, data) ==
ERC1155TokenReceiver.onERC1155Received.selector,
"UNSAFE_RECIPIENT"
);
}
function safeBatchTransferFrom(
address from,
address to,
uint256[] calldata ids,
uint256[] calldata amounts,
bytes calldata data
) public virtual {
require(ids.length == amounts.length, "LENGTH_MISMATCH");
require(msg.sender == from || isApprovedForAll[from][msg.sender], "NOT_AUTHORIZED");
// Storing these outside the loop saves ~15 gas per iteration.
uint256 id;
uint256 amount;
for (uint256 i = 0; i < ids.length; ) {
id = ids[i];
amount = amounts[i];
balanceOf[from][id] -= amount;
balanceOf[to][id] += amount;
// An array can't have a total length
// larger than the max uint256 value.
unchecked {
++i;
}
}
emit TransferBatch(msg.sender, from, to, ids, amounts);
require(
to.code.length == 0
? to != address(0)
: ERC1155TokenReceiver(to).onERC1155BatchReceived(msg.sender, from, ids, amounts, data) ==
ERC1155TokenReceiver.onERC1155BatchReceived.selector,
"UNSAFE_RECIPIENT"
);
}
function balanceOfBatch(address[] calldata owners, uint256[] calldata ids)
public
view
virtual
returns (uint256[] memory balances)
{
require(owners.length == ids.length, "LENGTH_MISMATCH");
balances = new uint256[](owners.length);
// Unchecked because the only math done is incrementing
// the array index counter which cannot possibly overflow.
unchecked {
for (uint256 i = 0; i < owners.length; ++i) {
balances[i] = balanceOf[owners[i]][ids[i]];
}
}
}
/*//////////////////////////////////////////////////////////////
ERC165 LOGIC
//////////////////////////////////////////////////////////////*/
function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
return
interfaceId == 0x01ffc9a7 || // ERC165 Interface ID for ERC165
interfaceId == 0xd9b67a26 || // ERC165 Interface ID for ERC1155
interfaceId == 0x0e89341c; // ERC165 Interface ID for ERC1155MetadataURI
}
/*//////////////////////////////////////////////////////////////
INTERNAL MINT/BURN LOGIC
//////////////////////////////////////////////////////////////*/
function _mint(
address to,
uint256 id,
uint256 amount,
bytes memory data
) internal virtual {
balanceOf[to][id] += amount;
emit TransferSingle(msg.sender, address(0), to, id, amount);
require(
to.code.length == 0
? to != address(0)
: ERC1155TokenReceiver(to).onERC1155Received(msg.sender, address(0), id, amount, data) ==
ERC1155TokenReceiver.onERC1155Received.selector,
"UNSAFE_RECIPIENT"
);
}
function _batchMint(
address to,
uint256[] memory ids,
uint256[] memory amounts,
bytes memory data
) internal virtual {
uint256 idsLength = ids.length; // Saves MLOADs.
require(idsLength == amounts.length, "LENGTH_MISMATCH");
for (uint256 i = 0; i < idsLength; ) {
balanceOf[to][ids[i]] += amounts[i];
// An array can't have a total length
// larger than the max uint256 value.
unchecked {
++i;
}
}
emit TransferBatch(msg.sender, address(0), to, ids, amounts);
require(
to.code.length == 0
? to != address(0)
: ERC1155TokenReceiver(to).onERC1155BatchReceived(msg.sender, address(0), ids, amounts, data) ==
ERC1155TokenReceiver.onERC1155BatchReceived.selector,
"UNSAFE_RECIPIENT"
);
}
function _batchBurn(
address from,
uint256[] memory ids,
uint256[] memory amounts
) internal virtual {
uint256 idsLength = ids.length; // Saves MLOADs.
require(idsLength == amounts.length, "LENGTH_MISMATCH");
for (uint256 i = 0; i < idsLength; ) {
balanceOf[from][ids[i]] -= amounts[i];
// An array can't have a total length
// larger than the max uint256 value.
unchecked {
++i;
}
}
emit TransferBatch(msg.sender, from, address(0), ids, amounts);
}
function _burn(
address from,
uint256 id,
uint256 amount
) internal virtual {
balanceOf[from][id] -= amount;
emit TransferSingle(msg.sender, from, address(0), id, amount);
}
}
/// @notice A generic interface for a contract which properly accepts ERC1155 tokens.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC1155.sol)
abstract contract ERC1155TokenReceiver {
function onERC1155Received(
address,
address,
uint256,
uint256,
bytes calldata
) external virtual returns (bytes4) {
return ERC1155TokenReceiver.onERC1155Received.selector;
}
function onERC1155BatchReceived(
address,
address,
uint256[] calldata,
uint256[] calldata,
bytes calldata
) external virtual returns (bytes4) {
return ERC1155TokenReceiver.onERC1155BatchReceived.selector;
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
interface IWETH9 {
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address recipient, uint256 amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(address src, address dst, uint256 wad) external returns (bool);
function deposit() external payable;
function withdraw(uint256 wad) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @title AllowanceTransfer
/// @notice Handles ERC20 token permissions through signature based allowance setting and ERC20 token transfers by checking allowed amounts
/// @dev Requires user's token approval on the Permit2 contract
interface IAllowanceTransfer {
/// @notice Thrown when an allowance on a token has expired.
/// @param deadline The timestamp at which the allowed amount is no longer valid
error AllowanceExpired(uint256 deadline);
/// @notice Thrown when an allowance on a token has been depleted.
/// @param amount The maximum amount allowed
error InsufficientAllowance(uint256 amount);
/// @notice Thrown when too many nonces are invalidated.
error ExcessiveInvalidation();
/// @notice Emits an event when the owner successfully invalidates an ordered nonce.
event NonceInvalidation(
address indexed owner,
address indexed token,
address indexed spender,
uint48 newNonce,
uint48 oldNonce
);
/// @notice Emits an event when the owner successfully sets permissions on a token for the spender.
event Approval(
address indexed owner,
address indexed token,
address indexed spender,
uint160 amount,
uint48 expiration
);
/// @notice Emits an event when the owner successfully sets permissions using a permit signature on a token for the spender.
event Permit(
address indexed owner,
address indexed token,
address indexed spender,
uint160 amount,
uint48 expiration,
uint48 nonce
);
/// @notice Emits an event when the owner sets the allowance back to 0 with the lockdown function.
event Lockdown(address indexed owner, address token, address spender);
/// @notice The permit data for a token
struct PermitDetails {
// ERC20 token address
address token;
// the maximum amount allowed to spend
uint160 amount;
// timestamp at which a spender's token allowances become invalid
uint48 expiration;
// an incrementing value indexed per owner,token,and spender for each signature
uint48 nonce;
}
/// @notice The permit message signed for a single token allownce
struct PermitSingle {
// the permit data for a single token alownce
PermitDetails details;
// address permissioned on the allowed tokens
address spender;
// deadline on the permit signature
uint256 sigDeadline;
}
/// @notice The permit message signed for multiple token allowances
struct PermitBatch {
// the permit data for multiple token allowances
PermitDetails[] details;
// address permissioned on the allowed tokens
address spender;
// deadline on the permit signature
uint256 sigDeadline;
}
/// @notice The saved permissions
/// @dev This info is saved per owner, per token, per spender and all signed over in the permit message
/// @dev Setting amount to type(uint160).max sets an unlimited approval
struct PackedAllowance {
// amount allowed
uint160 amount;
// permission expiry
uint48 expiration;
// an incrementing value indexed per owner,token,and spender for each signature
uint48 nonce;
}
/// @notice A token spender pair.
struct TokenSpenderPair {
// the token the spender is approved
address token;
// the spender address
address spender;
}
/// @notice Details for a token transfer.
struct AllowanceTransferDetails {
// the owner of the token
address from;
// the recipient of the token
address to;
// the amount of the token
uint160 amount;
// the token to be transferred
address token;
}
/// @notice A mapping from owner address to token address to spender address to PackedAllowance struct, which contains details and conditions of the approval.
/// @notice The mapping is indexed in the above order see: allowance[ownerAddress][tokenAddress][spenderAddress]
/// @dev The packed slot holds the allowed amount, expiration at which the allowed amount is no longer valid, and current nonce thats updated on any signature based approvals.
function allowance(address, address, address) external view returns (uint160, uint48, uint48);
/// @notice Approves the spender to use up to amount of the specified token up until the expiration
/// @param token The token to approve
/// @param spender The spender address to approve
/// @param amount The approved amount of the token
/// @param expiration The timestamp at which the approval is no longer valid
/// @dev The packed allowance also holds a nonce, which will stay unchanged in approve
/// @dev Setting amount to type(uint160).max sets an unlimited approval
function approve(address token, address spender, uint160 amount, uint48 expiration) external;
/// @notice Permit a spender to a given amount of the owners token via the owner's EIP-712 signature
/// @dev May fail if the owner's nonce was invalidated in-flight by invalidateNonce
/// @param owner The owner of the tokens being approved
/// @param permitSingle Data signed over by the owner specifying the terms of approval
/// @param signature The owner's signature over the permit data
function permit(address owner, PermitSingle memory permitSingle, bytes calldata signature) external;
/// @notice Permit a spender to the signed amounts of the owners tokens via the owner's EIP-712 signature
/// @dev May fail if the owner's nonce was invalidated in-flight by invalidateNonce
/// @param owner The owner of the tokens being approved
/// @param permitBatch Data signed over by the owner specifying the terms of approval
/// @param signature The owner's signature over the permit data
function permit(address owner, PermitBatch memory permitBatch, bytes calldata signature) external;
/// @notice Transfer approved tokens from one address to another
/// @param from The address to transfer from
/// @param to The address of the recipient
/// @param amount The amount of the token to transfer
/// @param token The token address to transfer
/// @dev Requires the from address to have approved at least the desired amount
/// of tokens to msg.sender.
function transferFrom(address from, address to, uint160 amount, address token) external;
/// @notice Transfer approved tokens in a batch
/// @param transferDetails Array of owners, recipients, amounts, and tokens for the transfers
/// @dev Requires the from addresses to have approved at least the desired amount
/// of tokens to msg.sender.
function transferFrom(AllowanceTransferDetails[] calldata transferDetails) external;
/// @notice Enables performing a "lockdown" of the sender's Permit2 identity
/// by batch revoking approvals
/// @param approvals Array of approvals to revoke.
function lockdown(TokenSpenderPair[] calldata approvals) external;
/// @notice Invalidate nonces for a given (token, spender) pair
/// @param token The token to invalidate nonces for
/// @param spender The spender to invalidate nonces for
/// @param newNonce The new nonce to set. Invalidates all nonces less than it.
/// @dev Can't invalidate more than 2**16 nonces per transaction.
function invalidateNonces(address token, address spender, uint48 newNonce) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import {ERC20} from "solmate/tokens/ERC20.sol";
import {SafeTransferLib} from "./SafeTransferLib.sol";
library UniERC20 {
using SafeTransferLib for address;
address internal constant ZERO_ADDRESS = address(0);
address internal constant ETH_ADDRESS = address(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE);
address internal constant MATIC_ADDRESS = address(0x0000000000000000000000000000000000001010);
function uniApprove(address token, address to, uint256 amount) internal {
if (!isETH(token)) {
if (amount == 0) {
token.safeApproveWithRetry(to, 0);
} else {
uint256 allowance = ERC20(token).allowance(address(this), to);
token.safeApproveWithRetry(to, amount);
}
}
}
function uniApproveMax(address token, address to, uint256 amount) internal {
if (!isETH(token)) {
uint256 allowance = ERC20(token).allowance(address(this), to);
token.safeApproveWithRetry(to, type(uint256).max);
}
}
function uniTransfer(address token, address payable to, uint256 amount) internal {
if (amount > 0) {
if (isETH(token)) {
to.call{value: amount}("");
} else {
token.safeTransfer(to, amount);
}
}
}
function uniBalanceOf(address token, address account) internal view returns (uint256) {
if (isETH(token)) {
return account.balance;
} else {
return token.balanceOf(account);
}
}
function isETH(address token) internal pure returns (bool) {
return
address(token) == address(ETH_ADDRESS) ||
address(token) == address(MATIC_ADDRESS) ||
address(token) == address(ZERO_ADDRESS);
}
}{
"remappings": [
"ds-test/=lib/forge-std/lib/ds-test/src/",
"era-contracts/=lib/foundry-zksync-era/lib/era-contracts/",
"era-system-contracts/=lib/foundry-zksync-era/lib/era-system-contracts/",
"forge-gas-snapshot/=lib/permit2/lib/forge-gas-snapshot/src/",
"forge-std/=lib/forge-std/src/",
"foundry-zksync-era/=lib/foundry-zksync-era/",
"openzeppelin-contracts/=lib/permit2/lib/openzeppelin-contracts/",
"solidity-stringutils/=lib/foundry-zksync-era/lib/solidity-stringutils/",
"@connext/=node_modules/@connext/",
"@eth-optimism/=node_modules/@hop-protocol/sdk/node_modules/@eth-optimism/",
"create3-factory/=lib/create3-factory/src/",
"solmate/=lib/solmate/src/",
"permit2/=lib/permit2/",
"eth-gas-reporter/=node_modules/eth-gas-reporter/",
"celer-network/=lib/sgn-v2-contracts/",
"eisen/=contracts/",
"@1inch/=node_modules/@1inch/",
"@chainlink/=node_modules/@chainlink/",
"@ensdomains/=node_modules/@ensdomains/",
"@openzeppelin/=node_modules/@openzeppelin/",
"@uniswap/=node_modules/@uniswap/",
"base64-sol/=node_modules/base64-sol/",
"hardhat-deploy/=node_modules/hardhat-deploy/",
"hardhat/=node_modules/hardhat/",
"sgn-v2-contracts/=lib/sgn-v2-contracts/contracts/",
"solady-gas-optimization/=lib/solady-gas-optimization/",
"solady/=node_modules/solady/"
],
"optimizer": {
"enabled": true,
"runs": 100000
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "ipfs",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "paris",
"viaIR": true,
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"_contractOwner","type":"address"},{"internalType":"address","name":"_diamondCutFacet","type":"address"},{"internalType":"address","name":"weth9","type":"address"},{"internalType":"address","name":"permit2","type":"address"},{"internalType":"uint256","name":"fee","type":"uint256"}],"stateMutability":"payable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"errorCode","type":"uint256"}],"name":"DiamondError","type":"error"},{"inputs":[{"internalType":"uint256","name":"errorCode","type":"uint256"}],"name":"GenericError","type":"error"},{"stateMutability":"payable","type":"fallback"},{"inputs":[{"internalType":"address","name":"feeCollector","type":"address"}],"name":"changeFeeCollector","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"fee","type":"uint256"}],"name":"setFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]Contract Creation Code
60806001600160401b03601f6200110538819003918201601f191684019183831185841017620007a95780859260a0946040528339810103126200080057620000488262000845565b620000566020840162000845565b92620000656040820162000845565b906080620000766060830162000845565b9101517fc8fcad8db84d3cc18b4c41d551ea0ee66dd599cde068d998e57d5e09332c132080546001600160a01b039586166001600160a01b03198083168217909355604051909693949390918791167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a37f6a2b33915c87ebbd2e7a47520fe4aaa6f0e18ef6bdebd64915d7aeced08d44819182549160ff8360a01c1615600014620007ea57507f6a2b33915c87ebbd2e7a47520fe4aaa6f0e18ef6bdebd64915d7aeced08d4480805482166001600160a01b039788169081179091557f6a2b33915c87ebbd2e7a47520fe4aaa6f0e18ef6bdebd64915d7aeced08d447f805490921688179091557f6a2b33915c87ebbd2e7a47520fe4aaa6f0e18ef6bdebd64915d7aeced08d447e8590556001600160a81b031990911694909216938417600160a01b179055604051918252907f6d83ac5d952931e715f949a02837cfba419afcd18414531fb7a2d05238536fb890602090a4620001f762000825565b916001835260005b60208110620007bf57506200021362000825565b60018152602036818301376307e4c70760e21b62000231826200085a565b526200023c62000805565b6001600160a01b03909216825260006020830152604082015262000260836200085a565b526200026c826200085a565b50604051906020820190811182821017620007a957604052600081526000905b825182101562000638576020620002a483856200087e565b51015160038110156200062257806200040c57506001600160a01b03620002cc83856200087e565b515116926040620002de84836200087e565b51015190815115620003f3578415620003db576001600160a01b0385166000908152600080516020620010e583398151915260205260409020546001600160601b0316908115620003ca575b6000915b8351831015620003b8576001600160e01b03196200034d84866200087e565b51166000818152600080516020620010a583398151915260205260409020549091906001600160a01b03166200039f576200038c88826001946200098a565b81808060601b039101169201916200032e565b604051633827045760e11b815260036004820152602490fd5b5094505050906001905b01906200028c565b620003d586620008bc565b6200032a565b604051633827045760e11b8152600480820152602490fd5b604051633827045760e11b815260026004820152602490fd5b600181036200052c57506001600160a01b036200042a83856200087e565b5151169260406200043c84836200087e565b51015190815115620003f3578415620003db576001600160a01b0385166000908152600080516020620010e583398151915260205260409020546001600160601b03169081156200051b575b6000915b83518310156200050c576001600160e01b0319620004ab84866200087e565b51166000818152600080516020620010a583398151915260205260409020549091906001600160a01b03168881146200039f57888284620004f3600196620004f99562000a7b565b6200098a565b81808060601b039101169201916200048c565b509450505090600190620003c2565b6200052686620008bc565b62000488565b9092600291820362000609576001600160a01b036200054c84836200087e565b51511660406200055d85846200087e565b51015192835115620005f15750620005d85760005b8251811015620005cb57600190620005c46001600160e01b03196200059883876200087e565b511680600052600080516020620010a5833981519152602052838060a01b036040600020541662000a7b565b0162000572565b50929160019150620003c2565b604051633827045760e11b815260056004820152602490fd5b60249060405190633827045760e11b82526004820152fd5b604051633827045760e11b815260016004820152602490fd5b634e487b7160e01b600052602160045260246000fd5b8260405190606082016060835281518091526080830190602060808260051b8601019301916000905b8282106200070d5750505050600060208301528181036040830152825180825260005b818110620006f757508282826000602080957f8faa70878671ccd212d20771b795c50af8fd3ff6cf27f4bde57e5d4de0aeb6739897010152601f801991011601030190a151620006de576040516103a7908162000cfe8239f35b604051633827045760e11b815260096004820152602490fd5b8060208092870101518282860101520162000684565b858503607f19018152835180516001600160a01b031686526020810151949593949293919290600382101562000622576040916020840152015190606060408201526020608060608301928451809452019201906000905b808210620007855750505060208060019296019201920190929162000661565b82516001600160e01b03191684526020938401939092019160019091019062000765565b634e487b7160e01b600052604160045260246000fd5b602090620007cc62000805565b600081526000838201526060604082015282828701015201620001ff565b63948ac50360e01b815260016004820152602490fd5b600080fd5b60405190606082016001600160401b03811183821017620007a957604052565b60408051919082016001600160401b03811183821017620007a957604052565b51906001600160a01b03821682036200080057565b805115620008685760200190565b634e487b7160e01b600052603260045260246000fd5b8051821015620008685760209160051b010190565b600080516020620010c58339815191528054821015620008685760005260206000200190600090565b803b156200094d57600080516020620010c583398151915280546001600160a01b0383166000908152600080516020620010e583398151915260205260409020600101819055919068010000000000000000831015620007a957826200092c9160016200094b9501905562000893565b90919082549060031b9160018060a01b03809116831b921b1916179055565b565b604051633827045760e11b815260066004820152602490fd5b91909180548310156200086857600052601c60206000208360031c019260021b1690565b6001600160e01b031981166000818152600080516020620010a583398151915260208190526040822080546001600160a01b031660a09690961b6001600160a01b031916959095179094559194939092906001600160a01b0316808352600080516020620010e5833981519152602052604083208054919491906801000000000000000082101562000a67579662000a31826040979899600162000a4e9501815562000966565b90919063ffffffff83549160031b9260e01c831b921b1916179055565b82526020522080546001600160a01b0319169091179055565b634e487b7160e01b85526041600452602485fd5b9091906001600160a01b03908116801562000ce45730811462000ccb5763ffffffff60e01b809416600092818452600080516020620010a5833981519152926020918483526040948587205460a01c90838852600080516020620010e58339815191529586865287892054926000199b8c850194851162000cb757908991888c898c8980870362000c42575b50509052505050878752508789208054801562000c2e578c019062000b2d828262000966565b63ffffffff82549160031b1b19169055558852845286868120551562000b58575b5050505050509050565b600080516020620010c5833981519152805489810190811162000c1a57838852858552826001888a2001549180830362000be2575b505050805498891562000bce576001979899019162000bac8362000893565b909182549160031b1b1916905555855252822001558038808080808062000b4e565b634e487b7160e01b88526031600452602488fd5b62000bed9062000893565b90549060031b1c1662000c05816200092c8462000893565b88528585526001878920015538828162000b8d565b634e487b7160e01b88526011600452602488fd5b634e487b7160e01b8b52603160045260248bfd5b62000ca9978462000a319362000c668a948762000c7d9952828a5284842062000966565b90549060031b1c60e01b9788968352522062000966565b168b52838852898b2080546001600160a01b031660a09290921b6001600160a01b031916919091179055565b873880888c898c8962000b07565b634e487b7160e01b8b52601160045260248bfd5b604051633827045760e11b815260086004820152602490fd5b604051633827045760e11b815260076004820152602490fdfe6080604090808252600490813610156100ea575b5036156100e8576000917fffffffff00000000000000000000000000000000000000000000000000000000833516918284527fc8fcad8db84d3cc18b4c41d551ea0ee66dd599cde068d998e57d5e09332c131c60205273ffffffffffffffffffffffffffffffffffffffff82852054169182156100b8575050828091368280378136915af43d83803e156100a657503d90f35b63a0d0feeb146100b4573d90fd5b3d90f35b90600760249251917f704e08ae000000000000000000000000000000000000000000000000000000008352820152fd5b005b600090813560e01c90816369fe0e2d1461023d5750639245290d03610013579190346102395760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102395780359073ffffffffffffffffffffffffffffffffffffffff9081831680930361023557817fc8fcad8db84d3cc18b4c41d551ea0ee66dd599cde068d998e57d5e09332c13205416330361020557507fffffffffffffffffffffffff0000000000000000000000000000000000000000907f649c5e3d0ed183894196148e193af316452b0037e77d2ff0fef23b7dc722bed07f6a2b33915c87ebbd2e7a47520fe4aaa6f0e18ef6bdebd64915d7aeced08d447f9485549281519084168152856020820152a11617905580f35b602490601a8551917f948ac503000000000000000000000000000000000000000000000000000000008352820152fd5b8480fd5b8280fd5b905083833461036d5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261036d5780359273ffffffffffffffffffffffffffffffffffffffff7fc8fcad8db84d3cc18b4c41d551ea0ee66dd599cde068d998e57d5e09332c13205416330361034057506103e8831161031157507f5fc463da23c1b063e66f9e352006a7fbe8db7223c455dc429e881a2dfe2f94f17f6a2b33915c87ebbd2e7a47520fe4aaa6f0e18ef6bdebd64915d7aeced08d447e9182548151908152846020820152a15580f35b602d60249251917f948ac503000000000000000000000000000000000000000000000000000000008352820152fd5b601a6024927f948ac503000000000000000000000000000000000000000000000000000000008352820152fd5b8380fdfea2646970667358221220f0755c1faddf31ce09c53ce3df5040bc8bb0bdfbbfe376505b968ee07c7b933a64736f6c63430008130033c8fcad8db84d3cc18b4c41d551ea0ee66dd599cde068d998e57d5e09332c131cc8fcad8db84d3cc18b4c41d551ea0ee66dd599cde068d998e57d5e09332c131ec8fcad8db84d3cc18b4c41d551ea0ee66dd599cde068d998e57d5e09332c131d000000000000000000000000daf87a186345f26d107d000fad351e79ff696d2c00000000000000000000000036694e0ae8c66a978a198afc5e2fa67e33300a6d000000000000000000000000a51894664a773981c6c112c43ce576f315d5b1b6000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba30000000000000000000000000000000000000000000000000000000000000005
Deployed Bytecode
0x6080604090808252600490813610156100ea575b5036156100e8576000917fffffffff00000000000000000000000000000000000000000000000000000000833516918284527fc8fcad8db84d3cc18b4c41d551ea0ee66dd599cde068d998e57d5e09332c131c60205273ffffffffffffffffffffffffffffffffffffffff82852054169182156100b8575050828091368280378136915af43d83803e156100a657503d90f35b63a0d0feeb146100b4573d90fd5b3d90f35b90600760249251917f704e08ae000000000000000000000000000000000000000000000000000000008352820152fd5b005b600090813560e01c90816369fe0e2d1461023d5750639245290d03610013579190346102395760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102395780359073ffffffffffffffffffffffffffffffffffffffff9081831680930361023557817fc8fcad8db84d3cc18b4c41d551ea0ee66dd599cde068d998e57d5e09332c13205416330361020557507fffffffffffffffffffffffff0000000000000000000000000000000000000000907f649c5e3d0ed183894196148e193af316452b0037e77d2ff0fef23b7dc722bed07f6a2b33915c87ebbd2e7a47520fe4aaa6f0e18ef6bdebd64915d7aeced08d447f9485549281519084168152856020820152a11617905580f35b602490601a8551917f948ac503000000000000000000000000000000000000000000000000000000008352820152fd5b8480fd5b8280fd5b905083833461036d5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261036d5780359273ffffffffffffffffffffffffffffffffffffffff7fc8fcad8db84d3cc18b4c41d551ea0ee66dd599cde068d998e57d5e09332c13205416330361034057506103e8831161031157507f5fc463da23c1b063e66f9e352006a7fbe8db7223c455dc429e881a2dfe2f94f17f6a2b33915c87ebbd2e7a47520fe4aaa6f0e18ef6bdebd64915d7aeced08d447e9182548151908152846020820152a15580f35b602d60249251917f948ac503000000000000000000000000000000000000000000000000000000008352820152fd5b601a6024927f948ac503000000000000000000000000000000000000000000000000000000008352820152fd5b8380fdfea2646970667358221220f0755c1faddf31ce09c53ce3df5040bc8bb0bdfbbfe376505b968ee07c7b933a64736f6c63430008130033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000daf87a186345f26d107d000fad351e79ff696d2c00000000000000000000000036694e0ae8c66a978a198afc5e2fa67e33300a6d000000000000000000000000a51894664a773981c6c112c43ce576f315d5b1b6000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba30000000000000000000000000000000000000000000000000000000000000005
-----Decoded View---------------
Arg [0] : _contractOwner (address): 0xdAf87a186345f26d107d000fAD351E79Ff696d2C
Arg [1] : _diamondCutFacet (address): 0x36694E0AE8c66A978A198AfC5E2FA67E33300A6D
Arg [2] : weth9 (address): 0xA51894664A773981C6C112C43ce576f315d5b1B6
Arg [3] : permit2 (address): 0x000000000022D473030F116dDEE9F6B43aC78BA3
Arg [4] : fee (uint256): 5
-----Encoded View---------------
5 Constructor Arguments found :
Arg [0] : 000000000000000000000000daf87a186345f26d107d000fad351e79ff696d2c
Arg [1] : 00000000000000000000000036694e0ae8c66a978a198afc5e2fa67e33300a6d
Arg [2] : 000000000000000000000000a51894664a773981c6c112c43ce576f315d5b1b6
Arg [3] : 000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba3
Arg [4] : 0000000000000000000000000000000000000000000000000000000000000005
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.