Source Code
Latest 1 from a total of 1 transactions
| Transaction Hash |
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
| Initialize | 16593154 | 340 days ago | IN | 0 FRAX | 0.00000012 |
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Cross-Chain Transactions
Loading...
Loading
Contract Name:
BranchPort
Compiler Version
v0.8.19+commit.7dd6d404
Optimization Enabled:
Yes with 1000000 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {Ownable} from "lib/solady/src/auth/Ownable.sol";
import {SafeTransferLib} from "lib/solady/src/utils/SafeTransferLib.sol";
import {ERC20} from "lib/solmate/src/tokens/ERC20.sol";
import {ReentrancyGuard} from "lib/solmate/src/utils/ReentrancyGuard.sol";
import {BridgeAgentConstants} from "./interfaces/BridgeAgentConstants.sol";
import {IBranchPort} from "./interfaces/IBranchPort.sol";
import {IPortStrategy} from "./interfaces/IPortStrategy.sol";
import {AddressCodeSize} from "./lib/AddressCodeSize.sol";
import {ERC20hToken} from "./token/ERC20hToken.sol";
import {ReservesRatio} from "./lib/ReservesRatio.sol";
/// @title Branch Port - Omnichain Token Management Contract
/// @author MaiaDAO
contract BranchPort is Ownable, ReentrancyGuard, IBranchPort, BridgeAgentConstants {
using SafeTransferLib for address;
using AddressCodeSize for address;
/*///////////////////////////////////////////////////////////////
CORE ROUTER STATE
///////////////////////////////////////////////////////////////*/
/// @notice Local Core Branch Router Address.
address public coreBranchRouterAddress;
/*///////////////////////////////////////////////////////////////
BRIDGE AGENT STATE
///////////////////////////////////////////////////////////////*/
/// @notice Mapping from Underlying Address to isUnderlying (bool).
mapping(address bridgeAgent => bool isActiveBridgeAgent) public isBridgeAgent;
/// @notice Branch Routers deployed in branch chain.
address[] public bridgeAgents;
/*///////////////////////////////////////////////////////////////
BRIDGE AGENT FACTORIES STATE
///////////////////////////////////////////////////////////////*/
/// @notice Mapping from Underlying Address to isUnderlying (bool).
mapping(address bridgeAgentFactory => bool isActiveBridgeAgentFactory) public isBridgeAgentFactory;
/*///////////////////////////////////////////////////////////////
STRATEGY TOKENS STATE
///////////////////////////////////////////////////////////////*/
/// @notice Returns true if Strategy Token Address is active for usage in Port Strategies.
mapping(address token => bool allowsStrategies) public isStrategyToken;
/// @notice Returns a given token's total debt incurred by Port Strategies.
mapping(address token => uint256 debt) public getStrategyTokenDebt;
/// @notice Returns the minimum ratio of a given Strategy Token the Port should hold.
mapping(address token => uint256 minimumReserveRatio) public getMinimumTokenReserveRatio;
/*///////////////////////////////////////////////////////////////
PORT STRATEGIES STATE
///////////////////////////////////////////////////////////////*/
/// @notice Returns true if Port Strategy is allowed to manage a given Strategy Token.
mapping(address strategy => mapping(address token => bool isActiveStrategy)) public isPortStrategy;
/// @notice The amount of Strategy Token debt a given Port Strategy has.
mapping(address strategy => mapping(address token => uint256 debt)) public getPortStrategyTokenDebt;
/// @notice The last time a given Port Strategy managed a given Strategy Token.
mapping(address strategy => mapping(address token => uint256 lastManaged)) public lastManaged;
/// @notice The reserves ratio limit a given Port Strategy must wait before managing a Strategy Token.
mapping(address strategy => mapping(address token => uint256 reserveRatioManagementLimit)) public
strategyReserveRatioManagementLimit;
/// @notice The time limit a given Port Strategy must wait before managing a Strategy Token.
mapping(address strategy => mapping(address token => uint256 dailyLimitAmount)) public strategyDailyLimitAmount;
/// @notice The amount of a Strategy Token a given Port Strategy can manage.
mapping(address strategy => mapping(address token => uint256 dailyLimitRemaining)) public
strategyDailyLimitRemaining;
/*///////////////////////////////////////////////////////////////
CONSTANTS
///////////////////////////////////////////////////////////////*/
uint256 internal constant DIVISIONER = 1e4;
/// Fraxtal Incentives
address public constant DELEGATION_REGISTRY = 0x098c837FeF2e146e96ceAF58A10F68Fc6326DC4C;
address public constant DELEGATEE = 0x88E07a0457aA113AB910103d9a01217315DA1C98;
/*///////////////////////////////////////////////////////////////
CONSTRUCTOR
///////////////////////////////////////////////////////////////*/
/**
* @notice Constructor for the Branch Port Contract.
* @param _owner Address of the Owner.
*/
constructor(address _owner) {
require(_owner != address(0), "Owner is zero address");
_initializeOwner(_owner);
/// Fraxtal Incentives
DELEGATION_REGISTRY.call(abi.encodeWithSignature("setDelegationForSelf(address)", DELEGATEE));
DELEGATION_REGISTRY.call(abi.encodeWithSignature("disableSelfManagingDelegations()"));
DELEGATION_REGISTRY.call(abi.encodeWithSignature("disableDelegationManagement()"));
}
/*///////////////////////////////////////////////////////////////
FALLBACK FUNCTIONS
///////////////////////////////////////////////////////////////*/
receive() external payable {}
/*///////////////////////////////////////////////////////////////
INITIALIZATION FUNCTIONS
///////////////////////////////////////////////////////////////*/
/**
* @notice Initializes the Branch Port.
* @param _coreBranchRouter Address of the Core Branch Router.
* @param _bridgeAgentFactory Address of the Bridge Agent Factory.
*/
function initialize(address _coreBranchRouter, address _bridgeAgentFactory) external virtual onlyOwner {
require(_coreBranchRouter != address(0), "CoreBranchRouter is zero address");
require(_bridgeAgentFactory != address(0), "BridgeAgentFactory is zero address");
renounceOwnership();
coreBranchRouterAddress = _coreBranchRouter;
isBridgeAgentFactory[_bridgeAgentFactory] = true;
}
/*///////////////////////////////////////////////////////////////
PORT STRATEGY FUNCTIONS
///////////////////////////////////////////////////////////////*/
/// @inheritdoc IBranchPort
function manage(address _token, uint256 _amount) external override nonReentrant requiresPortStrategy(_token) {
// Cache Strategy Token Global Debt
uint256 _strategyTokenDebt = getStrategyTokenDebt[_token];
uint256 _portStrategyTokenDebt = getPortStrategyTokenDebt[msg.sender][_token];
// Check if request would surpass the tokens minimum reserves
// Check if request would surpass the Port Strategy's reserve ratio management limit
_enforceReservesLimit(_token, _amount, _strategyTokenDebt, _portStrategyTokenDebt);
// Check if request would surpass the Port Strategy's daily limit
_enforceTimeLimit(_token, _amount);
// Update Strategy Token Global Debt
getStrategyTokenDebt[_token] = _strategyTokenDebt + _amount;
// Update Port Strategy Token Debt
getPortStrategyTokenDebt[msg.sender][_token] = _portStrategyTokenDebt + _amount;
// Transfer tokens to Port Strategy for management
_token.safeTransfer(msg.sender, _amount);
// Emit DebtCreated event
emit DebtCreated(msg.sender, _token, _amount);
}
/// @inheritdoc IBranchPort
function replenishReserves(address _token, uint256 _amount) external override nonReentrant {
// Check if `_amount` is greater than zero.
if (_amount == 0) revert InvalidAmount();
if (getPortStrategyTokenDebt[msg.sender][_token] < _amount) revert InsufficientDebt();
// Get current balance of _token
uint256 currBalance = ERC20(_token).balanceOf(address(this));
// Withdraw tokens from startegy
IPortStrategy(msg.sender).withdraw(address(this), _token, _amount);
// Check if _token balance has increased by _amount
require(ERC20(_token).balanceOf(address(this)) - currBalance == _amount, "Port Strategy Withdraw Failed");
// Update Port Strategy Token Debt. Will underflow if not enough debt to repay.
getPortStrategyTokenDebt[msg.sender][_token] -= _amount;
// Update Strategy Token Global Debt. Will underflow if not enough debt to repay.
getStrategyTokenDebt[_token] -= _amount;
// Emit DebtRepaid event
emit DebtRepaid(msg.sender, _token, _amount);
}
/// @inheritdoc IBranchPort
function replenishReserves(address _strategy, address _token) external override nonReentrant {
// Cache Strategy Token Global Debt
uint256 strategyTokenDebt = getStrategyTokenDebt[_token];
// Get current balance of _token
uint256 currBalance = ERC20(_token).balanceOf(address(this));
// Get reserves lacking
uint256 reservesLacking = _reservesLacking(_token, currBalance, strategyTokenDebt);
// Cache Port Strategy Token Debt
uint256 portStrategyTokenDebt = getPortStrategyTokenDebt[_strategy][_token];
// Check if `portStrategyTokenDebt` is greater than zero.
if (portStrategyTokenDebt == 0) revert InsufficientDebt();
// Calculate amount to withdraw. The lesser of reserves lacking or Strategy Token Global Debt.
uint256 amountToWithdraw = portStrategyTokenDebt < reservesLacking ? portStrategyTokenDebt : reservesLacking;
// Check if amountToWithdraw is greater than zero.
if (amountToWithdraw == 0) revert InvalidAmount();
// Withdraw tokens from startegy
IPortStrategy(_strategy).withdraw(address(this), _token, amountToWithdraw);
// Update Port Strategy Token Debt
getPortStrategyTokenDebt[_strategy][_token] -= amountToWithdraw;
// Update Strategy Token Global Debt
getStrategyTokenDebt[_token] = strategyTokenDebt - amountToWithdraw;
// Check if _token balance has increased by _amount
require(
ERC20(_token).balanceOf(address(this)) - currBalance == amountToWithdraw, "Port Strategy Withdraw Failed"
);
// Emit DebtRepaid event
emit DebtRepaid(_strategy, _token, amountToWithdraw);
}
/*///////////////////////////////////////////////////////////////
EXTERNAL FUNCTIONS
///////////////////////////////////////////////////////////////*/
/// @inheritdoc IBranchPort
function withdraw(address _recipient, address _underlyingAddress, uint256 _deposit)
public
virtual
override
nonReentrant
requiresBridgeAgent
{
_underlyingAddress.safeTransfer(_recipient, _deposit);
}
/// @inheritdoc IBranchPort
function bridgeIn(address _recipient, address _localAddress, uint256 _amount)
external
override
nonReentrant
requiresBridgeAgent
{
_bridgeIn(_recipient, _localAddress, _amount);
}
/// @inheritdoc IBranchPort
function bridgeInMultiple(
address _recipient,
address[] memory _localAddresses,
address[] memory _underlyingAddresses,
uint256[] memory _amounts,
uint256[] memory _deposits
) external override nonReentrant requiresBridgeAgent {
// Cache Length
uint256 length = _localAddresses.length;
// Loop through token inputs
for (uint256 i = 0; i < length;) {
// Check if hTokens are being bridged in
if (_amounts[i] - _deposits[i] > 0) {
unchecked {
_bridgeIn(_recipient, _localAddresses[i], _amounts[i] - _deposits[i]);
}
}
// Check if underlying tokens are being cleared
if (_deposits[i] > 0) {
_underlyingAddresses[i].safeTransfer(_recipient, _deposits[i]);
}
unchecked {
++i;
}
}
}
/// @inheritdoc IBranchPort
function bridgeOut(
address _depositor,
address _localAddress,
address _underlyingAddress,
uint256 _amount,
uint256 _deposit
) external override nonReentrant requiresBridgeAgent {
_bridgeOut(_depositor, _localAddress, _underlyingAddress, _amount, _deposit);
}
/// @inheritdoc IBranchPort
function bridgeOutMultiple(
address _depositor,
address[] memory _localAddresses,
address[] memory _underlyingAddresses,
uint256[] memory _amounts,
uint256[] memory _deposits
) external override nonReentrant requiresBridgeAgent {
// Cache Length
uint256 length = _localAddresses.length;
// Sanity Check input arrays
if (length > MAX_TOKENS_LENGTH) revert InvalidInputArrays();
if (length != _underlyingAddresses.length) revert InvalidInputArrays();
if (_underlyingAddresses.length != _amounts.length) revert InvalidInputArrays();
if (_amounts.length != _deposits.length) revert InvalidInputArrays();
// Loop through token inputs and bridge out
for (uint256 i = 0; i < length;) {
_bridgeOut(_depositor, _localAddresses[i], _underlyingAddresses[i], _amounts[i], _deposits[i]);
unchecked {
i++;
}
}
}
/*///////////////////////////////////////////////////////////////
BRIDGE AGENT FACTORIES FUNCTIONS
///////////////////////////////////////////////////////////////*/
/// @inheritdoc IBranchPort
function addBridgeAgent(address _bridgeAgent) external override requiresBridgeAgentFactory {
if (isBridgeAgent[_bridgeAgent]) revert AlreadyAddedBridgeAgent();
isBridgeAgent[_bridgeAgent] = true;
bridgeAgents.push(_bridgeAgent);
}
/*///////////////////////////////////////////////////////////////
ADMIN FUNCTIONS
///////////////////////////////////////////////////////////////*/
/// @inheritdoc IBranchPort
function toggleBridgeAgentFactory(address _newBridgeAgentFactory) external override requiresCoreRouter {
// Invert Bridge Agent Factory status
isBridgeAgentFactory[_newBridgeAgentFactory] = !isBridgeAgentFactory[_newBridgeAgentFactory];
emit BridgeAgentFactoryToggled(_newBridgeAgentFactory);
}
/// @inheritdoc IBranchPort
function toggleStrategyToken(address _token, uint256 _minimumReservesRatio) external override requiresCoreRouter {
// Check if token is already a strategy token
if (isStrategyToken[_token]) {
// If already added as a strategy token, remove it
isStrategyToken[_token] = false;
// Set minimumReservesRatio to 100% so all strategies can be forced to repay
_setStrategyTokenMinimumReservesRatio(_token, DIVISIONER);
// If not added as a strategy token
} else {
// Add token as a strategy token
isStrategyToken[_token] = true;
// Set minimumReservesRatio to _minimumReservesRatio
_setStrategyTokenMinimumReservesRatio(_token, _minimumReservesRatio);
}
}
/// @inheritdoc IBranchPort
function updateStrategyToken(address _token, uint256 _minimumReservesRatio) external override requiresCoreRouter {
// Check if token is already a strategy token
if (!isStrategyToken[_token]) revert UnrecognizedStrategyToken();
_setStrategyTokenMinimumReservesRatio(_token, _minimumReservesRatio);
}
function _setStrategyTokenMinimumReservesRatio(address _token, uint256 _minimumReservesRatio) internal {
ReservesRatio.checkReserveRatioLimit(_minimumReservesRatio);
// Set the Strategy Token's Minimum Reserves Ratio
getMinimumTokenReserveRatio[_token] = _minimumReservesRatio;
emit StrategyTokenUpdated(_token, _minimumReservesRatio);
}
/// @inheritdoc IBranchPort
function togglePortStrategy(
address _portStrategy,
address _token,
uint256 _dailyManagementLimit,
uint256 _reserveRatioManagementLimit
) external override requiresCoreRouter {
// Check if token is already a strategy token
if (isPortStrategy[_portStrategy][_token]) {
// If already added as a strategy token, remove it
isPortStrategy[_portStrategy][_token] = false;
// Set minimumReservesRatio to 100% so all strategies can be forced to repay
_setPortStrategySettings(_portStrategy, _token, 0, DIVISIONER);
// If not added as a strategy token
} else {
if (!isStrategyToken[_token]) revert UnrecognizedStrategyToken();
// Add token as a strategy token
isPortStrategy[_portStrategy][_token] = true;
// Set minimumReservesRatio to _minimumReservesRatio
_setPortStrategySettings(_portStrategy, _token, _dailyManagementLimit, _reserveRatioManagementLimit);
}
}
/// @inheritdoc IBranchPort
function updatePortStrategy(
address _portStrategy,
address _token,
uint256 _dailyManagementLimit,
uint256 _reserveRatioManagementLimit
) external override requiresCoreRouter {
if (!isStrategyToken[_token]) revert UnrecognizedStrategyToken();
if (!isPortStrategy[_portStrategy][_token]) revert UnrecognizedPortStrategy();
_setPortStrategySettings(_portStrategy, _token, _dailyManagementLimit, _reserveRatioManagementLimit);
}
function _setPortStrategySettings(
address _portStrategy,
address _token,
uint256 _dailyManagementLimit,
uint256 _reserveRatioManagementLimit
) internal {
ReservesRatio.checkReserveRatioLimit(_reserveRatioManagementLimit);
// Set the Strategy Token's Minimum Reserves Ratio
strategyDailyLimitAmount[_portStrategy][_token] = _dailyManagementLimit;
// Set the Strategy Token's Maximum Reserves Ratio Management Limit
strategyReserveRatioManagementLimit[_portStrategy][_token] = _reserveRatioManagementLimit;
emit PortStrategyUpdated(_portStrategy, _token, _dailyManagementLimit, _reserveRatioManagementLimit);
}
/// @inheritdoc IBranchPort
function setCoreBranchRouter(address _coreBranchRouter, address _coreBranchBridgeAgent)
external
override
requiresCoreRouter
{
require(_coreBranchRouter != address(0), "New CoreRouter address is zero");
require(_coreBranchBridgeAgent != address(0), "New Bridge Agent address is zero");
coreBranchRouterAddress = _coreBranchRouter;
isBridgeAgent[_coreBranchBridgeAgent] = true;
bridgeAgents.push(_coreBranchBridgeAgent);
emit CoreBranchSet(_coreBranchRouter, _coreBranchBridgeAgent);
}
/// @inheritdoc IBranchPort
function sweep(address _recipient) external override requiresCoreRouter {
// Safe Transfer All ETH
_recipient.safeTransferAllETH();
}
/*///////////////////////////////////////////////////////////////
INTERNAL VIEW FUNCTIONS
///////////////////////////////////////////////////////////////*/
/**
* @notice Internal function to check if a Port Strategy has reached its reserves management limit.
* @param _token Address of a given Strategy Token.
* @param _amount Amount of tokens to be bridged in.
* @param _strategyTokenDebt Total token debt incurred by a given Port Token.
* @param _portStrategyTokenDebt Total token debt incurred by a given Port Strategy for a Token.
*/
function _enforceReservesLimit(
address _token,
uint256 _amount,
uint256 _strategyTokenDebt,
uint256 _portStrategyTokenDebt
) internal view {
uint256 currBalance = ERC20(_token).balanceOf(address(this));
uint256 totalTokenBalance = currBalance + _strategyTokenDebt;
// Check if request would surpass the tokens minimum reserves
if ((_amount + _minimumReserves(_token, totalTokenBalance)) > currBalance) {
revert InsufficientReserves();
}
// Check if request would surpass the Port Strategy's reserve ratio management limit
if ((_amount + _portStrategyTokenDebt) > _strategyReserveManagementLimit(_token, totalTokenBalance)) {
revert ExceedsReserveRatioManagementLimit();
}
}
/**
* @notice Returns amount of Strategy Tokens needed to reach minimum reserves
* @param _token Address of a given Strategy Token.
* @param _currBalance Current balance of a given Strategy Token.
* @param _strategyTokenDebt Total token debt incurred by Port Strategies.
* @return uint256 excess reserves
*/
function _reservesLacking(address _token, uint256 _currBalance, uint256 _strategyTokenDebt)
internal
view
returns (uint256)
{
uint256 minReserves = _minimumReserves(_token, _currBalance + _strategyTokenDebt);
unchecked {
return _currBalance < minReserves ? minReserves - _currBalance : 0;
}
}
/**
* @notice Internal function to return the minimum amount of reserves of a given Strategy Token the Port should hold.
* @param _token Address of a given Strategy Token.
* @param _totalTokenBalance Total balance of a given Strategy Token.
* @return uint256 minimum reserves
*/
function _minimumReserves(address _token, uint256 _totalTokenBalance) internal view returns (uint256) {
return (_totalTokenBalance * getMinimumTokenReserveRatio[_token]) / DIVISIONER;
}
/**
* @notice Internal function to return the maximum amount of reserves management limit.
* @param _token address being managed.
* @param _totalTokenBalance Total balance of a given Strategy Token.
* @return uint256 Maximum reserves amount management limit
*/
function _strategyReserveManagementLimit(address _token, uint256 _totalTokenBalance)
internal
view
returns (uint256)
{
return
(_totalTokenBalance * (DIVISIONER - strategyReserveRatioManagementLimit[msg.sender][_token])) / DIVISIONER;
}
/*///////////////////////////////////////////////////////////////
INTERNAL FUNCTIONS
///////////////////////////////////////////////////////////////*/
/**
* @notice Internal function to check and update the Port Strategy's daily management limit.
* @param _token address being managed.
* @param _amount of token being requested.
*/
function _enforceTimeLimit(address _token, uint256 _amount) internal {
uint256 dailyLimit = strategyDailyLimitRemaining[msg.sender][_token];
if (block.timestamp - lastManaged[msg.sender][_token] >= 1 days) {
dailyLimit = strategyDailyLimitAmount[msg.sender][_token];
unchecked {
lastManaged[msg.sender][_token] = (block.timestamp / 1 days) * 1 days;
}
}
strategyDailyLimitRemaining[msg.sender][_token] = dailyLimit - _amount;
}
/**
* @notice Internal function to bridge in hTokens.
* @param _recipient address of the recipient.
* @param _localAddress address of the hToken.
* @param _amount amount of hTokens to bridge in.
*/
function _bridgeIn(address _recipient, address _localAddress, uint256 _amount) internal virtual {
ERC20hToken(_localAddress).mint(_recipient, _amount);
}
/**
* @notice Internal function to bridge out hTokens and underlying tokens.
* @param _depositor address of the depositor.
* @param _localAddress address of the hToken.
* @param _underlyingAddress address of the underlying token.
* @param _amount total amount of tokens to bridge out.
* @param _deposit amount of underlying tokens to bridge out.
*/
function _bridgeOut(
address _depositor,
address _localAddress,
address _underlyingAddress,
uint256 _amount,
uint256 _deposit
) internal virtual {
// Check if hTokens are being bridged out
if (_amount - _deposit > 0) {
unchecked {
ERC20hToken(_localAddress).burn(_depositor, _amount - _deposit);
}
}
// Check if underlying tokens are being bridged out
if (_deposit > 0) {
// Check if underlying address is a contract
if (_underlyingAddress.isEOA()) revert InvalidUnderlyingAddress();
_underlyingAddress.safeTransferFrom(_depositor, address(this), _deposit);
}
}
/*///////////////////////////////////////////////////////////////
MODIFIERS
///////////////////////////////////////////////////////////////*/
/// @notice Modifier that verifies msg sender is the Branch Chain's Core Root Router.
modifier requiresCoreRouter() {
if (msg.sender != coreBranchRouterAddress) revert UnrecognizedCore();
_;
}
/// @notice Modifier that verifies msg sender is an active Bridge Agent.
modifier requiresBridgeAgent() {
if (!isBridgeAgent[msg.sender]) revert UnrecognizedBridgeAgent();
_;
}
/// @notice Modifier that verifies msg sender is an active Bridge Agent Factory.
modifier requiresBridgeAgentFactory() {
if (!isBridgeAgentFactory[msg.sender]) revert UnrecognizedBridgeAgentFactory();
_;
}
/// @notice Modifier that require msg sender to be an active Port Strategy
modifier requiresPortStrategy(address _token) {
if (!isStrategyToken[_token]) revert UnrecognizedStrategyToken();
if (!isPortStrategy[msg.sender][_token]) revert UnrecognizedPortStrategy();
_;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Simple single owner authorization mixin.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/auth/Ownable.sol)
///
/// @dev Note:
/// This implementation does NOT auto-initialize the owner to `msg.sender`.
/// You MUST call the `_initializeOwner` in the constructor / initializer.
///
/// While the ownable portion follows
/// [EIP-173](https://eips.ethereum.org/EIPS/eip-173) for compatibility,
/// the nomenclature for the 2-step ownership handover may be unique to this codebase.
abstract contract Ownable {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The caller is not authorized to call the function.
error Unauthorized();
/// @dev The `newOwner` cannot be the zero address.
error NewOwnerIsZeroAddress();
/// @dev The `pendingOwner` does not have a valid handover request.
error NoHandoverRequest();
/// @dev Cannot double-initialize.
error AlreadyInitialized();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EVENTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The ownership is transferred from `oldOwner` to `newOwner`.
/// This event is intentionally kept the same as OpenZeppelin's Ownable to be
/// compatible with indexers and [EIP-173](https://eips.ethereum.org/EIPS/eip-173),
/// despite it not being as lightweight as a single argument event.
event OwnershipTransferred(address indexed oldOwner, address indexed newOwner);
/// @dev An ownership handover to `pendingOwner` has been requested.
event OwnershipHandoverRequested(address indexed pendingOwner);
/// @dev The ownership handover to `pendingOwner` has been canceled.
event OwnershipHandoverCanceled(address indexed pendingOwner);
/// @dev `keccak256(bytes("OwnershipTransferred(address,address)"))`.
uint256 private constant _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE =
0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0;
/// @dev `keccak256(bytes("OwnershipHandoverRequested(address)"))`.
uint256 private constant _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE =
0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d;
/// @dev `keccak256(bytes("OwnershipHandoverCanceled(address)"))`.
uint256 private constant _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE =
0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* STORAGE */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The owner slot is given by:
/// `bytes32(~uint256(uint32(bytes4(keccak256("_OWNER_SLOT_NOT")))))`.
/// It is intentionally chosen to be a high value
/// to avoid collision with lower slots.
/// The choice of manual storage layout is to enable compatibility
/// with both regular and upgradeable contracts.
bytes32 internal constant _OWNER_SLOT =
0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927;
/// The ownership handover slot of `newOwner` is given by:
/// ```
/// mstore(0x00, or(shl(96, user), _HANDOVER_SLOT_SEED))
/// let handoverSlot := keccak256(0x00, 0x20)
/// ```
/// It stores the expiry timestamp of the two-step ownership handover.
uint256 private constant _HANDOVER_SLOT_SEED = 0x389a75e1;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* INTERNAL FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Override to return true to make `_initializeOwner` prevent double-initialization.
function _guardInitializeOwner() internal pure virtual returns (bool guard) {}
/// @dev Initializes the owner directly without authorization guard.
/// This function must be called upon initialization,
/// regardless of whether the contract is upgradeable or not.
/// This is to enable generalization to both regular and upgradeable contracts,
/// and to save gas in case the initial owner is not the caller.
/// For performance reasons, this function will not check if there
/// is an existing owner.
function _initializeOwner(address newOwner) internal virtual {
if (_guardInitializeOwner()) {
/// @solidity memory-safe-assembly
assembly {
let ownerSlot := _OWNER_SLOT
if sload(ownerSlot) {
mstore(0x00, 0x0dc149f0) // `AlreadyInitialized()`.
revert(0x1c, 0x04)
}
// Clean the upper 96 bits.
newOwner := shr(96, shl(96, newOwner))
// Store the new value.
sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner))))
// Emit the {OwnershipTransferred} event.
log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)
}
} else {
/// @solidity memory-safe-assembly
assembly {
// Clean the upper 96 bits.
newOwner := shr(96, shl(96, newOwner))
// Store the new value.
sstore(_OWNER_SLOT, newOwner)
// Emit the {OwnershipTransferred} event.
log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)
}
}
}
/// @dev Sets the owner directly without authorization guard.
function _setOwner(address newOwner) internal virtual {
if (_guardInitializeOwner()) {
/// @solidity memory-safe-assembly
assembly {
let ownerSlot := _OWNER_SLOT
// Clean the upper 96 bits.
newOwner := shr(96, shl(96, newOwner))
// Emit the {OwnershipTransferred} event.
log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)
// Store the new value.
sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner))))
}
} else {
/// @solidity memory-safe-assembly
assembly {
let ownerSlot := _OWNER_SLOT
// Clean the upper 96 bits.
newOwner := shr(96, shl(96, newOwner))
// Emit the {OwnershipTransferred} event.
log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)
// Store the new value.
sstore(ownerSlot, newOwner)
}
}
}
/// @dev Throws if the sender is not the owner.
function _checkOwner() internal view virtual {
/// @solidity memory-safe-assembly
assembly {
// If the caller is not the stored owner, revert.
if iszero(eq(caller(), sload(_OWNER_SLOT))) {
mstore(0x00, 0x82b42900) // `Unauthorized()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Returns how long a two-step ownership handover is valid for in seconds.
/// Override to return a different value if needed.
/// Made internal to conserve bytecode. Wrap it in a public function if needed.
function _ownershipHandoverValidFor() internal view virtual returns (uint64) {
return 48 * 3600;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* PUBLIC UPDATE FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Allows the owner to transfer the ownership to `newOwner`.
function transferOwnership(address newOwner) public payable virtual onlyOwner {
/// @solidity memory-safe-assembly
assembly {
if iszero(shl(96, newOwner)) {
mstore(0x00, 0x7448fbae) // `NewOwnerIsZeroAddress()`.
revert(0x1c, 0x04)
}
}
_setOwner(newOwner);
}
/// @dev Allows the owner to renounce their ownership.
function renounceOwnership() public payable virtual onlyOwner {
_setOwner(address(0));
}
/// @dev Request a two-step ownership handover to the caller.
/// The request will automatically expire in 48 hours (172800 seconds) by default.
function requestOwnershipHandover() public payable virtual {
unchecked {
uint256 expires = block.timestamp + _ownershipHandoverValidFor();
/// @solidity memory-safe-assembly
assembly {
// Compute and set the handover slot to `expires`.
mstore(0x0c, _HANDOVER_SLOT_SEED)
mstore(0x00, caller())
sstore(keccak256(0x0c, 0x20), expires)
// Emit the {OwnershipHandoverRequested} event.
log2(0, 0, _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE, caller())
}
}
}
/// @dev Cancels the two-step ownership handover to the caller, if any.
function cancelOwnershipHandover() public payable virtual {
/// @solidity memory-safe-assembly
assembly {
// Compute and set the handover slot to 0.
mstore(0x0c, _HANDOVER_SLOT_SEED)
mstore(0x00, caller())
sstore(keccak256(0x0c, 0x20), 0)
// Emit the {OwnershipHandoverCanceled} event.
log2(0, 0, _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE, caller())
}
}
/// @dev Allows the owner to complete the two-step ownership handover to `pendingOwner`.
/// Reverts if there is no existing ownership handover requested by `pendingOwner`.
function completeOwnershipHandover(address pendingOwner) public payable virtual onlyOwner {
/// @solidity memory-safe-assembly
assembly {
// Compute and set the handover slot to 0.
mstore(0x0c, _HANDOVER_SLOT_SEED)
mstore(0x00, pendingOwner)
let handoverSlot := keccak256(0x0c, 0x20)
// If the handover does not exist, or has expired.
if gt(timestamp(), sload(handoverSlot)) {
mstore(0x00, 0x6f5e8818) // `NoHandoverRequest()`.
revert(0x1c, 0x04)
}
// Set the handover slot to 0.
sstore(handoverSlot, 0)
}
_setOwner(pendingOwner);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* PUBLIC READ FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the owner of the contract.
function owner() public view virtual returns (address result) {
/// @solidity memory-safe-assembly
assembly {
result := sload(_OWNER_SLOT)
}
}
/// @dev Returns the expiry timestamp for the two-step ownership handover to `pendingOwner`.
function ownershipHandoverExpiresAt(address pendingOwner)
public
view
virtual
returns (uint256 result)
{
/// @solidity memory-safe-assembly
assembly {
// Compute the handover slot.
mstore(0x0c, _HANDOVER_SLOT_SEED)
mstore(0x00, pendingOwner)
// Load the handover slot.
result := sload(keccak256(0x0c, 0x20))
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* MODIFIERS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Marks a function as only callable by the owner.
modifier onlyOwner() virtual {
_checkOwner();
_;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SafeTransferLib.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
/// @author Permit2 operations from (https://github.com/Uniswap/permit2/blob/main/src/libraries/Permit2Lib.sol)
///
/// @dev Note:
/// - For ETH transfers, please use `forceSafeTransferETH` for DoS protection.
/// - 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();
/// @dev The Permit2 operation has failed.
error Permit2Failed();
/// @dev The Permit2 amount must be less than `2**160 - 1`.
error Permit2AmountOverflow();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTANTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Suggested gas stipend for contract receiving ETH that disallows any storage writes.
uint256 internal constant GAS_STIPEND_NO_STORAGE_WRITES = 2300;
/// @dev Suggested gas stipend for contract receiving ETH to perform a few
/// storage reads and writes, but low enough to prevent griefing.
uint256 internal constant GAS_STIPEND_NO_GRIEF = 100000;
/// @dev The unique EIP-712 domain domain separator for the DAI token contract.
bytes32 internal constant DAI_DOMAIN_SEPARATOR =
0xdbb8cf42e1ecb028be3f3dbc922e1d878b963f411dc388ced501601c60f7c6f7;
/// @dev The address for the WETH9 contract on Ethereum mainnet.
address internal constant WETH9 = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
/// @dev The canonical Permit2 address.
/// [Github](https://github.com/Uniswap/permit2)
/// [Etherscan](https://etherscan.io/address/0x000000000022D473030F116dDEE9F6B43aC78BA3)
address internal constant PERMIT2 = 0x000000000022D473030F116dDEE9F6B43aC78BA3;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ETH OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
// If the ETH transfer MUST succeed with a reasonable gas budget, use the force variants.
//
// The regular variants:
// - Forwards all remaining gas to the target.
// - Reverts if the target reverts.
// - Reverts if the current contract has insufficient balance.
//
// The force variants:
// - Forwards with an optional gas stipend
// (defaults to `GAS_STIPEND_NO_GRIEF`, which is sufficient for most cases).
// - If the target reverts, or if the gas stipend is exhausted,
// creates a temporary contract to force send the ETH via `SELFDESTRUCT`.
// Future compatible with `SENDALL`: https://eips.ethereum.org/EIPS/eip-4758.
// - Reverts if the current contract has insufficient balance.
//
// The try variants:
// - Forwards with a mandatory gas stipend.
// - Instead of reverting, returns whether the transfer succeeded.
/// @dev Sends `amount` (in wei) ETH to `to`.
function safeTransferETH(address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
if iszero(call(gas(), to, amount, codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Sends all the ETH in the current contract to `to`.
function safeTransferAllETH(address to) internal {
/// @solidity memory-safe-assembly
assembly {
// Transfer all the ETH and check if it succeeded or not.
if iszero(call(gas(), to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Force sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
function forceSafeTransferETH(address to, uint256 amount, uint256 gasStipend) internal {
/// @solidity memory-safe-assembly
assembly {
if lt(selfbalance(), amount) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
revert(0x1c, 0x04)
}
if iszero(call(gasStipend, to, amount, codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, to) // Store the address in scratch space.
mstore8(0x0b, 0x73) // Opcode `PUSH20`.
mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
if iszero(create(amount, 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
}
}
}
/// @dev Force sends all the ETH in the current contract to `to`, with a `gasStipend`.
function forceSafeTransferAllETH(address to, uint256 gasStipend) internal {
/// @solidity memory-safe-assembly
assembly {
if iszero(call(gasStipend, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, to) // Store the address in scratch space.
mstore8(0x0b, 0x73) // Opcode `PUSH20`.
mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
if iszero(create(selfbalance(), 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
}
}
}
/// @dev Force sends `amount` (in wei) ETH to `to`, with `GAS_STIPEND_NO_GRIEF`.
function forceSafeTransferETH(address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
if lt(selfbalance(), amount) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
revert(0x1c, 0x04)
}
if iszero(call(GAS_STIPEND_NO_GRIEF, to, amount, codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, to) // Store the address in scratch space.
mstore8(0x0b, 0x73) // Opcode `PUSH20`.
mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
if iszero(create(amount, 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
}
}
}
/// @dev Force sends all the ETH in the current contract to `to`, with `GAS_STIPEND_NO_GRIEF`.
function forceSafeTransferAllETH(address to) internal {
/// @solidity memory-safe-assembly
assembly {
// forgefmt: disable-next-item
if iszero(call(GAS_STIPEND_NO_GRIEF, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, to) // Store the address in scratch space.
mstore8(0x0b, 0x73) // Opcode `PUSH20`.
mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
if iszero(create(selfbalance(), 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
}
}
}
/// @dev Sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
function trySafeTransferETH(address to, uint256 amount, uint256 gasStipend)
internal
returns (bool success)
{
/// @solidity memory-safe-assembly
assembly {
success := call(gasStipend, to, amount, codesize(), 0x00, codesize(), 0x00)
}
}
/// @dev Sends all the ETH in the current contract to `to`, with a `gasStipend`.
function trySafeTransferAllETH(address to, uint256 gasStipend)
internal
returns (bool success)
{
/// @solidity memory-safe-assembly
assembly {
success := call(gasStipend, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ERC20 OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Sends `amount` of ERC20 `token` from `from` to `to`.
/// Reverts upon failure.
///
/// The `from` account must have at least `amount` approved for
/// the current contract to manage.
function safeTransferFrom(address token, address from, address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x60, amount) // Store the `amount` argument.
mstore(0x40, to) // Store the `to` argument.
mstore(0x2c, shl(96, from)) // Store the `from` argument.
mstore(0x0c, 0x23b872dd000000000000000000000000) // `transferFrom(address,address,uint256)`.
// 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 `from` to `to`.
///
/// The `from` account must have at least `amount` approved for the current contract to manage.
function trySafeTransferFrom(address token, address from, address to, uint256 amount)
internal
returns (bool success)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x60, amount) // Store the `amount` argument.
mstore(0x40, to) // Store the `to` argument.
mstore(0x2c, shl(96, from)) // Store the `from` argument.
mstore(0x0c, 0x23b872dd000000000000000000000000) // `transferFrom(address,address,uint256)`.
success :=
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(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, codesize(), 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( // The arguments of `mul` are evaluated from right to left.
mload(0x20),
and( // The arguments of `and` are evaluated from right to left.
gt(returndatasize(), 0x1f), // At least 32 bytes returned.
staticcall(gas(), token, 0x10, 0x24, 0x20, 0x20)
)
)
}
}
/// @dev Sends `amount` of ERC20 `token` from `from` to `to`.
/// If the initial attempt fails, try to use Permit2 to transfer the token.
/// Reverts upon failure.
///
/// The `from` account must have at least `amount` approved for the current contract to manage.
function safeTransferFrom2(address token, address from, address to, uint256 amount) internal {
if (!trySafeTransferFrom(token, from, to, amount)) {
permit2TransferFrom(token, from, to, amount);
}
}
/// @dev Sends `amount` of ERC20 `token` from `from` to `to` via Permit2.
/// Reverts upon failure.
function permit2TransferFrom(address token, address from, address to, uint256 amount)
internal
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(add(m, 0x74), shr(96, shl(96, token)))
mstore(add(m, 0x54), amount)
mstore(add(m, 0x34), to)
mstore(add(m, 0x20), shl(96, from))
// `transferFrom(address,address,uint160,address)`.
mstore(m, 0x36c78516000000000000000000000000)
let p := PERMIT2
let exists := eq(chainid(), 1)
if iszero(exists) { exists := iszero(iszero(extcodesize(p))) }
if iszero(and(call(gas(), p, 0, add(m, 0x10), 0x84, codesize(), 0x00), exists)) {
mstore(0x00, 0x7939f4248757f0fd) // `TransferFromFailed()` or `Permit2AmountOverflow()`.
revert(add(0x18, shl(2, iszero(iszero(shr(160, amount))))), 0x04)
}
}
}
/// @dev Permit a user to spend a given amount of
/// another user's tokens via native EIP-2612 permit if possible, falling
/// back to Permit2 if native permit fails or is not implemented on the token.
function permit2(
address token,
address owner,
address spender,
uint256 amount,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) internal {
bool success;
/// @solidity memory-safe-assembly
assembly {
for {} shl(96, xor(token, WETH9)) {} {
mstore(0x00, 0x3644e515) // `DOMAIN_SEPARATOR()`.
if iszero(
and( // The arguments of `and` are evaluated from right to left.
lt(iszero(mload(0x00)), eq(returndatasize(), 0x20)), // Returns 1 non-zero word.
// Gas stipend to limit gas burn for tokens that don't refund gas when
// an non-existing function is called. 5K should be enough for a SLOAD.
staticcall(5000, token, 0x1c, 0x04, 0x00, 0x20)
)
) { break }
// After here, we can be sure that token is a contract.
let m := mload(0x40)
mstore(add(m, 0x34), spender)
mstore(add(m, 0x20), shl(96, owner))
mstore(add(m, 0x74), deadline)
if eq(mload(0x00), DAI_DOMAIN_SEPARATOR) {
mstore(0x14, owner)
mstore(0x00, 0x7ecebe00000000000000000000000000) // `nonces(address)`.
mstore(add(m, 0x94), staticcall(gas(), token, 0x10, 0x24, add(m, 0x54), 0x20))
mstore(m, 0x8fcbaf0c000000000000000000000000) // `IDAIPermit.permit`.
// `nonces` is already at `add(m, 0x54)`.
// `1` is already stored at `add(m, 0x94)`.
mstore(add(m, 0xb4), and(0xff, v))
mstore(add(m, 0xd4), r)
mstore(add(m, 0xf4), s)
success := call(gas(), token, 0, add(m, 0x10), 0x104, codesize(), 0x00)
break
}
mstore(m, 0xd505accf000000000000000000000000) // `IERC20Permit.permit`.
mstore(add(m, 0x54), amount)
mstore(add(m, 0x94), and(0xff, v))
mstore(add(m, 0xb4), r)
mstore(add(m, 0xd4), s)
success := call(gas(), token, 0, add(m, 0x10), 0xe4, codesize(), 0x00)
break
}
}
if (!success) simplePermit2(token, owner, spender, amount, deadline, v, r, s);
}
/// @dev Simple permit on the Permit2 contract.
function simplePermit2(
address token,
address owner,
address spender,
uint256 amount,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) internal {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(m, 0x927da105) // `allowance(address,address,address)`.
{
let addressMask := shr(96, not(0))
mstore(add(m, 0x20), and(addressMask, owner))
mstore(add(m, 0x40), and(addressMask, token))
mstore(add(m, 0x60), and(addressMask, spender))
mstore(add(m, 0xc0), and(addressMask, spender))
}
let p := mul(PERMIT2, iszero(shr(160, amount)))
if iszero(
and( // The arguments of `and` are evaluated from right to left.
gt(returndatasize(), 0x5f), // Returns 3 words: `amount`, `expiration`, `nonce`.
staticcall(gas(), p, add(m, 0x1c), 0x64, add(m, 0x60), 0x60)
)
) {
mstore(0x00, 0x6b836e6b8757f0fd) // `Permit2Failed()` or `Permit2AmountOverflow()`.
revert(add(0x18, shl(2, iszero(p))), 0x04)
}
mstore(m, 0x2b67b570) // `Permit2.permit` (PermitSingle variant).
// `owner` is already `add(m, 0x20)`.
// `token` is already at `add(m, 0x40)`.
mstore(add(m, 0x60), amount)
mstore(add(m, 0x80), 0xffffffffffff) // `expiration = type(uint48).max`.
// `nonce` is already at `add(m, 0xa0)`.
// `spender` is already at `add(m, 0xc0)`.
mstore(add(m, 0xe0), deadline)
mstore(add(m, 0x100), 0x100) // `signature` offset.
mstore(add(m, 0x120), 0x41) // `signature` length.
mstore(add(m, 0x140), r)
mstore(add(m, 0x160), s)
mstore(add(m, 0x180), shl(248, v))
if iszero(call(gas(), p, 0, add(m, 0x1c), 0x184, codesize(), 0x00)) {
mstore(0x00, 0x6b836e6b) // `Permit2Failed()`.
revert(0x1c, 0x04)
}
}
}
}// 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 Gas optimized reentrancy protection for smart contracts.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/ReentrancyGuard.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/security/ReentrancyGuard.sol)
abstract contract ReentrancyGuard {
uint256 private locked = 1;
modifier nonReentrant() virtual {
require(locked == 1, "REENTRANCY");
locked = 2;
_;
locked = 1;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @title Bridge Agent Constants Contract
* @author MaiaDAO
* @notice Constants for use in Bridge Agent and Bridge Agent Executor contracts.
* @dev Used for encoding / decoding of the cross-chain messages and state management.
*/
contract BridgeAgentConstants {
/*///////////////////////////////////////////////////////////////
SETTLEMENT / DEPOSIT EXECUTION STATUS CONSTANTS
///////////////////////////////////////////////////////////////*/
/// @notice Indicates that a settlement or deposit is ready to be executed.
uint8 internal constant STATUS_READY = 0;
/// @notice Indicates that a settlement or deposit has been executed.
uint8 internal constant STATUS_DONE = 1;
/// @notice Indicates that a settlement or deposit has failed and can only be retrieved.
uint8 internal constant STATUS_RETRIEVE = 2;
/*///////////////////////////////////////////////////////////////
SETTLEMENT / DEPOSIT REDEEM STATUS CONSTANTS
///////////////////////////////////////////////////////////////*/
/// @notice Indicates that the request for settlement or deposit was successful.
uint8 internal constant STATUS_SUCCESS = 0;
/// @notice Indicates that the request for settlement or deposit has failed.
uint8 internal constant STATUS_FAILED = 1;
/*///////////////////////////////////////////////////////////////
DEPOSIT SIGNATURE CONSTANTS
///////////////////////////////////////////////////////////////*/
/// @notice Indicates that the deposit has been signed.
uint8 internal constant SIGNED_DEPOSIT = 1;
/// @notice Indicates that the deposit has not been signed.
uint8 internal constant UNSIGNED_DEPOSIT = 0;
/*///////////////////////////////////////////////////////////////
PAYLOAD ENCODING / DECODING POSITIONAL CONSTANTS
///////////////////////////////////////////////////////////////*/
/// @notice Defines the position in bytes where the payload starts after the flag byte.
/// Also used to offset number of assets in the payload.
uint256 internal constant PARAMS_START = 1;
/// @notice Defines the position in bytes where the signed payload starts after the flag byte and user address.
uint256 internal constant PARAMS_START_SIGNED = 21;
/// @notice Defines the position in bytes where token-related information starts, after flag byte and nonce.
uint256 internal constant PARAMS_TKN_START = 5;
/// @notice Defines the position in bytes where signed token-related information starts.
/// @dev After flag byte, user and nonce.
uint256 internal constant PARAMS_TKN_START_SIGNED = 25;
/// @notice Size in bytes for standard Ethereum types / slot size (like uint256).
uint256 internal constant PARAMS_ENTRY_SIZE = 32;
/// @notice Size in bytes for an Ethereum address.
uint256 internal constant PARAMS_ADDRESS_SIZE = 20;
/// @notice Size in bytes for a single set of packed token-related parameters (hToken, token, amount, deposit).
uint256 internal constant PARAMS_TKN_SET_SIZE = 109;
/// @notice Size in bytes for an entry of multiple-token-related parameters, taking padding into account.
/// @dev (hToken, token, amount, deposit)
uint256 internal constant PARAMS_TKN_SET_SIZE_MULTIPLE = 128;
/// @notice Offset in bytes to mark the end of the standard (deposit related) parameters in the payload.
uint256 internal constant PARAMS_END_OFFSET = 6;
/// @notice Offset in bytes to mark the end of the standard (deposit related) signed parameters in the payload.
uint256 internal constant PARAMS_END_SIGNED_OFFSET = 26;
/// @notice Offset in bytes to mark the end of the standard (settlement related) parameters in the payload.
uint256 internal constant PARAMS_SETTLEMENT_OFFSET = 129;
/*///////////////////////////////////////////////////////////////
DEPOSIT / SETTLEMENT LIMITATION CONSTANTS
///////////////////////////////////////////////////////////////*/
/// @notice Maximum length of tokens allowed for deposit or settlement.
uint256 internal constant MAX_TOKENS_LENGTH = 255;
/*///////////////////////////////////////////////////////////////
MINIMUM EXECUTION GAS CONSTANTS
///////////////////////////////////////////////////////////////*/
/// @notice Minimum gas required to safely fail execution.
uint256 internal constant BASE_EXECUTION_FAILED_GAS = 15_000;
/// @notice Minimum gas required for a fallback request.
uint256 internal constant BASE_FALLBACK_GAS = 140_000;
//--------------------BRANCH: Deposit------------------------------
/// @notice Minimum gas required for a callOut request.
uint256 internal constant BRANCH_BASE_CALL_OUT_GAS = 100_000;
/// @notice Minimum gas required for a callOutDepositSingle request.
uint256 internal constant BRANCH_BASE_CALL_OUT_DEPOSIT_SINGLE_GAS = 150_000;
/// @notice Minimum gas required for a callOutDepositMultiple request.
uint256 internal constant BRANCH_BASE_CALL_OUT_DEPOSIT_MULTIPLE_GAS = 200_000;
/// @notice Minimum gas required for a callOut request.
uint256 internal constant BRANCH_BASE_CALL_OUT_SIGNED_GAS = 100_000;
/// @notice Minimum gas required for a callOutDepositSingle request.
uint256 internal constant BRANCH_BASE_CALL_OUT_SIGNED_DEPOSIT_SINGLE_GAS = 150_000;
/// @notice Minimum gas required for a callOutDepositMultiple request.
uint256 internal constant BRANCH_BASE_CALL_OUT_SIGNED_DEPOSIT_MULTIPLE_GAS = 200_000;
//---------------------ROOT: Settlement----------------------------
/// @notice Minimum gas required for a callOut request.
uint256 internal constant ROOT_BASE_CALL_OUT_GAS = 100_000;
/// @notice Minimum gas required for a callOutDepositSingle request.
uint256 internal constant ROOT_BASE_CALL_OUT_SETTLEMENT_SINGLE_GAS = 150_000;
/// @notice Minimum gas required for a callOutDepositMultiple request.
uint256 internal constant ROOT_BASE_CALL_OUT_SETTLEMENT_MULTIPLE_GAS = 200_000;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @title Branch Port - Omnichain Token Management Contract
* @author MaiaDAO
* @notice Ulyses `Port` implementation for Branch Chain deployment. This contract is used to manage the deposit and
* withdrawal of underlying assets from the Branch Chain in response to Branch Bridge Agent requests.
* Manages Bridge Agents and their factories as well as the chain's strategies and their tokens.
*/
interface IBranchPort {
/*///////////////////////////////////////////////////////////////
VIEW FUNCTIONS
///////////////////////////////////////////////////////////////*/
/**
* @notice Returns true if the address is a Bridge Agent.
* @param _bridgeAgent Bridge Agent address.
* @return bool .
*/
function isBridgeAgent(address _bridgeAgent) external view returns (bool);
/**
* @notice Returns true if the address is a Strategy Token.
* @param _token token address.
* @return bool.
*/
function isStrategyToken(address _token) external view returns (bool);
/**
* @notice Returns true if the address is a Port Strategy.
* @param _strategy strategy address.
* @param _token token address.
* @return bool.
*/
function isPortStrategy(address _strategy, address _token) external view returns (bool);
/**
* @notice Returns true if the address is a Bridge Agent Factory.
* @param _bridgeAgentFactory Bridge Agent Factory address.
* @return bool.
*/
function isBridgeAgentFactory(address _bridgeAgentFactory) external view returns (bool);
/*///////////////////////////////////////////////////////////////
PORT STRATEGY MANAGEMENT
///////////////////////////////////////////////////////////////*/
/**
* @notice Allows active Port Strategy addresses to withdraw assets.
* @param _token token address.
* @param _amount amount of tokens.
*/
function manage(address _token, uint256 _amount) external;
/**
* @notice allow strategy address to repay borrowed reserves with reserves.
* @param _amount amount of tokens to repay.
* @param _token address of the token to repay.
* @dev must be called by the port strategy itself.
*/
function replenishReserves(address _token, uint256 _amount) external;
/**
* @notice allow anyone to request repayment of a strategy's reserves if Port is under minimum reserves ratio.
* @param _strategy address of the strategy to repay.
* @param _token address of the token to repay.
* @dev can be called by anyone to ensure availability of service.
*/
function replenishReserves(address _strategy, address _token) external;
/*///////////////////////////////////////////////////////////////
hTOKEN MANAGEMENT
///////////////////////////////////////////////////////////////*/
/**
* @notice Function to withdraw underlying / native token amount from Port to Branch Bridge Agent.
* @param _recipient address of the underlying token receiver.
* @param _underlyingAddress underlying token address.
* @param _amount amount of tokens.
* @dev must be called by the bridge agent itself. Matches the burning of global hTokens in root chain.
*/
function withdraw(address _recipient, address _underlyingAddress, uint256 _amount) external;
/**
* @notice Function to mint hToken amount to Branch Bridge Agent.
* @param _recipient address of the hToken receiver.
* @param _localAddress hToken address.
* @param _amount amount of hTokens.
* @dev must be called by the bridge agent itself. Matches the storage of global hTokens in root port.
*/
function bridgeIn(address _recipient, address _localAddress, uint256 _amount) external;
/**
* @notice Function to withdraw underlying / native tokens and mint local hTokens to Branch Bridge Agent.
* @param _recipient address of the token receiver.
* @param _localAddresses local hToken addresses.
* @param _underlyingAddresses underlying token addresses.
* @param _amounts total amount of tokens.
* @param _deposits amount of underlying tokens.
*/
function bridgeInMultiple(
address _recipient,
address[] memory _localAddresses,
address[] memory _underlyingAddresses,
uint256[] memory _amounts,
uint256[] memory _deposits
) external;
/**
* @notice Function to deposit underlying / native tokens in Port and burn hTokens.
* @param _depositor address of the token depositor.
* @param _localAddress local hToken addresses.
* @param _underlyingAddress underlying token addresses.
* @param _amount total amount of tokens.
* @param _deposit amount of underlying tokens.
*/
function bridgeOut(
address _depositor,
address _localAddress,
address _underlyingAddress,
uint256 _amount,
uint256 _deposit
) external;
/**
* @notice Setter function to decrease local hToken supply.
* @param _depositor address of the token depositor.
* @param _localAddresses local hToken addresses.
* @param _underlyingAddresses underlying token addresses.
* @param _amounts total amount of tokens.
* @param _deposits amount of underlying tokens.
*/
function bridgeOutMultiple(
address _depositor,
address[] memory _localAddresses,
address[] memory _underlyingAddresses,
uint256[] memory _amounts,
uint256[] memory _deposits
) external;
/*///////////////////////////////////////////////////////////////
ADMIN FUNCTIONS
///////////////////////////////////////////////////////////////*/
/**
* @notice Adds a new bridge agent address to the branch port.
* @param _bridgeAgent address of the bridge agent to add to the Port
*/
function addBridgeAgent(address _bridgeAgent) external;
/**
* @notice Toggle a given bridge agent factory. If it's active, it will de-activate it and vice-versa.
* @param _bridgeAgentFactory address of the bridge agent factory to add to the Port
*/
function toggleBridgeAgentFactory(address _bridgeAgentFactory) external;
/**
* @notice Toggle a given strategy token. If it's active, it will de-activate it and vice-versa.
* @param _token address of the token to add to the Strategy Tokens
* @param _minimumReservesRatio minimum reserves ratio for the token
* @dev Must be between 7000 and 10000 (70% and 100%). Can be any value if the token is being de-activated.
*/
function toggleStrategyToken(address _token, uint256 _minimumReservesRatio) external;
/**
* @notice Update an active strategy token's minimum reserves ratio. If it is not active, it will revert.
* @param _token address of the token to add to the Strategy Tokens
* @param _minimumReservesRatio minimum reserves ratio for the token
* @dev Must be between 7000 and 10000 (70% and 100%). Can be any value if the token is being de-activated.
*/
function updateStrategyToken(address _token, uint256 _minimumReservesRatio) external;
/**
* @notice Add or Remove a Port Strategy.
* @param _portStrategy Address of the Port Strategy to be added for use in Branch strategies.
* @param _underlyingToken Address of the underlying token to be added for use in Branch strategies.
* @param _dailyManagementLimit Daily management limit of the given token for the Port Strategy.
* @param _reserveRatioManagementLimit Total reserves management limit of the given token for the Port Strategy.
* @dev Must be between 7000 and 10000 (70% and 100%). Can be any value if the token is being de-activated.
*/
function togglePortStrategy(
address _portStrategy,
address _underlyingToken,
uint256 _dailyManagementLimit,
uint256 _reserveRatioManagementLimit
) external;
/**
* @notice Updates a Port Strategy.
* @param _portStrategy Address of the Port Strategy to be added for use in Branch strategies.
* @param _underlyingToken Address of the underlying token to be added for use in Branch strategies.
* @param _dailyManagementLimit Daily management limit of the given token for the Port Strategy.
* @param _reserveRatioManagementLimit Total reserves management limit of the given token for the Port Strategy.
* @dev Must be between 7000 and 10000 (70% and 100%). Can be any value if the token is being de-activated.
*/
function updatePortStrategy(
address _portStrategy,
address _underlyingToken,
uint256 _dailyManagementLimit,
uint256 _reserveRatioManagementLimit
) external;
/**
* @notice Sets the core branch router and bridge agent for the branch port.
* @param _coreBranchRouter address of the new core branch router
* @param _coreBranchBridgeAgent address of the new core branch bridge agent
*/
function setCoreBranchRouter(address _coreBranchRouter, address _coreBranchBridgeAgent) external;
/**
* @notice Allows governance to claim any native tokens accumulated from failed transactions.
* @param _recipient address to transfer ETH to.
*/
function sweep(address _recipient) external;
/*///////////////////////////////////////////////////////////////
EVENTS
///////////////////////////////////////////////////////////////*/
/// @notice Event emitted when a Port Strategy manages more reserves increasing its debt for a given token.
event DebtCreated(address indexed _strategy, address indexed _token, uint256 _amount);
/// @notice Event emitted when a Port Strategy replenishes reserves decreasing its debt for a given token.
event DebtRepaid(address indexed _strategy, address indexed _token, uint256 _amount);
/// @notice Event emitted when Strategy Token has its details updated.
event StrategyTokenUpdated(address indexed _token, uint256 indexed _minimumReservesRatio);
/// @notice Event emitted when a Port Strategy has its details updated.
event PortStrategyUpdated(
address indexed _portStrategy,
address indexed _token,
uint256 indexed _dailyManagementLimit,
uint256 _reserveRatioManagementLimit
);
/// @notice Event emitted when a Branch Bridge Agent Factory is toggled on or off.
event BridgeAgentFactoryToggled(address indexed _bridgeAgentFactory);
/// @notice Event emitted when a Bridge Agent is toggled on or off.
event BridgeAgentToggled(address indexed _bridgeAgent);
/// @notice Event emitted when a Core Branch Router and Bridge Agent are set.
event CoreBranchSet(address indexed _coreBranchRouter, address indexed _coreBranchBridgeAgent);
/*///////////////////////////////////////////////////////////////
ERRORS
///////////////////////////////////////////////////////////////*/
/// @notice Error emitted when Bridge Agent is already added.
error AlreadyAddedBridgeAgent();
/// @notice Error emitted when Port Strategy request would exceed the Branch Port's minimum reserves.
error InsufficientReserves();
/// @notice Error emitted when not enough debt.
error InsufficientDebt();
/// @notice Error emitted when Port Strategy request would exceed it's reserve ratio management limit.
error ExceedsReserveRatioManagementLimit();
/// @notice Error emitted when amount requested is zero.
error InvalidAmount();
/// @notice Error emitted when token deposit arrays have different lengths.
error InvalidInputArrays();
/// @notice Error emitted when an invalid underlying token address is provided.
error InvalidUnderlyingAddress();
/// @notice Error emitted when caller is not the Core Branch Router.
error UnrecognizedCore();
/// @notice Error emitted when caller is not an active Branch Bridge Agent.
error UnrecognizedBridgeAgent();
/// @notice Error emitted when caller is not an active Branch Bridge Agent Factory.
error UnrecognizedBridgeAgentFactory();
/// @notice Error emitted when caller is not an active Port Strategy.
error UnrecognizedPortStrategy();
/// @notice Error emitted when caller is not an active Strategy Token.
error UnrecognizedStrategyToken();
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @title ERC20 hToken Branch Contract
* @author MaiaDAO.
* @notice ERC20 hToken contract deployed with the Ulysses Omnichain Liquidity System.
* ERC20 representation of a token deposited in a Branch Chain's Port.
* @dev If this is a root hToken, this asset is minted / burned in reflection of it's origin Branch Port balance.
* Should not be burned being stored in Root Port instead if Branch hToken mint is requested.
*/
interface IERC20hToken {
/*///////////////////////////////////////////////////////////////
ERC20 LOGIC
///////////////////////////////////////////////////////////////*/
/**
* @notice Function to mint tokens.
* @param account Address of the account to receive the tokens.
* @param amount Amount of tokens to be minted.
*/
function mint(address account, uint256 amount) external;
/**
* @notice Function to burn tokens.
* @param account Address of the account to burn the tokens from.
* @param amount Amount of tokens to be burned.
*/
function burn(address account, uint256 amount) external;
/*///////////////////////////////////////////////////////////////
ERRORS
///////////////////////////////////////////////////////////////*/
/// @notice Error thrown when the Port Address is the zero address.
error InvalidPortAddress();
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @title Port Strategy Interface
* @author MaiaDAO
* @notice Interface to be implemented by Brach Port Strategy contracts
* allowlisted by the chain's Branch Port to manage a limited amount
* of one or more Strategy Tokens.
*/
interface IPortStrategy {
/*///////////////////////////////////////////////////////////////
TOKEN MANAGEMENT
///////////////////////////////////////////////////////////////*/
/**
* @notice Function to withdraw underlying/native token amount back into Branch Port.
* @param _recipient hToken receiver.
* @param _token native token address.
* @param _amount amount of tokens.
*/
function withdraw(address _recipient, address _token, uint256 _amount) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @title Address Code Size Library
* @notice Library for checking the size of a contract's code.
* @dev Used for checking if an address is a contract or an EOA.
*/
library AddressCodeSize {
/*///////////////////////////////////////////////////////////////
PAYLOAD DECODING POSITIONAL CONSTANTS
///////////////////////////////////////////////////////////////*/
function isContract(address addr) internal view returns (bool) {
uint256 size;
assembly ("memory-safe") {
size := extcodesize(addr)
}
return size > 0;
}
function isEOA(address addr) internal view returns (bool) {
uint256 size;
assembly ("memory-safe") {
size := extcodesize(addr)
}
return size == 0;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
library ReservesRatio {
uint256 private constant DIVISIONER = 1e4;
uint256 private constant MIN_RESERVE_RATIO = 7e3;
function checkReserveRatioLimit(uint256 _reserveRatioManagementLimit) internal pure {
// Check if reserveRatioManagementLimit is less or equal to 100%
if (_reserveRatioManagementLimit > DIVISIONER) {
revert InvalidMinimumReservesRatio();
}
// Check if reserveRatioManagementLimit is greater than or equal to 70%
if (_reserveRatioManagementLimit < MIN_RESERVE_RATIO) {
revert InvalidMinimumReservesRatio();
}
}
/*///////////////////////////////////////////////////////////////
ERRORS
///////////////////////////////////////////////////////////////*/
/// @notice Error emitted when minimum reserves ratio is set too low.
error InvalidMinimumReservesRatio();
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {Ownable} from "lib/solady/src/auth/Ownable.sol";
import {ERC20} from "lib/solmate/src/tokens/ERC20.sol";
import {IERC20hToken} from "../interfaces/IERC20hToken.sol";
/// @title ERC20 hToken Contract
/// @author MaiaDAO
contract ERC20hToken is ERC20, Ownable, IERC20hToken {
/**
* @notice Constructor for the ERC20hToken branch or root Contract.
* @param _localPortAddress Address of the local Branch or Root Port Contract.
* @param _name Name of the Token.
* @param _symbol Symbol of the Token.
* @param _decimals Decimals of the Token.
*/
constructor(address _localPortAddress, string memory _name, string memory _symbol, uint8 _decimals)
ERC20(_name, _symbol, _decimals)
{
if (_localPortAddress == address(0)) revert InvalidPortAddress();
_initializeOwner(_localPortAddress);
}
/*///////////////////////////////////////////////////////////////
ERC20 LOGIC
///////////////////////////////////////////////////////////////*/
/// @inheritdoc IERC20hToken
function mint(address account, uint256 amount) external override onlyOwner {
_mint(account, amount);
}
/// @inheritdoc IERC20hToken
function burn(address account, uint256 amount) public override onlyOwner {
_burn(account, amount);
}
}{
"viaIR": true,
"optimizer": {
"enabled": true,
"runs": 1000000
},
"metadata": {
"bytecodeHash": "none"
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AlreadyAddedBridgeAgent","type":"error"},{"inputs":[],"name":"AlreadyInitialized","type":"error"},{"inputs":[],"name":"ExceedsReserveRatioManagementLimit","type":"error"},{"inputs":[],"name":"InsufficientDebt","type":"error"},{"inputs":[],"name":"InsufficientReserves","type":"error"},{"inputs":[],"name":"InvalidAmount","type":"error"},{"inputs":[],"name":"InvalidInputArrays","type":"error"},{"inputs":[],"name":"InvalidMinimumReservesRatio","type":"error"},{"inputs":[],"name":"InvalidUnderlyingAddress","type":"error"},{"inputs":[],"name":"NewOwnerIsZeroAddress","type":"error"},{"inputs":[],"name":"NoHandoverRequest","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"inputs":[],"name":"UnrecognizedBridgeAgent","type":"error"},{"inputs":[],"name":"UnrecognizedBridgeAgentFactory","type":"error"},{"inputs":[],"name":"UnrecognizedCore","type":"error"},{"inputs":[],"name":"UnrecognizedPortStrategy","type":"error"},{"inputs":[],"name":"UnrecognizedStrategyToken","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_bridgeAgentFactory","type":"address"}],"name":"BridgeAgentFactoryToggled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_bridgeAgent","type":"address"}],"name":"BridgeAgentToggled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_coreBranchRouter","type":"address"},{"indexed":true,"internalType":"address","name":"_coreBranchBridgeAgent","type":"address"}],"name":"CoreBranchSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_strategy","type":"address"},{"indexed":true,"internalType":"address","name":"_token","type":"address"},{"indexed":false,"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"DebtCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_strategy","type":"address"},{"indexed":true,"internalType":"address","name":"_token","type":"address"},{"indexed":false,"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"DebtRepaid","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipHandoverCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipHandoverRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_portStrategy","type":"address"},{"indexed":true,"internalType":"address","name":"_token","type":"address"},{"indexed":true,"internalType":"uint256","name":"_dailyManagementLimit","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_reserveRatioManagementLimit","type":"uint256"}],"name":"PortStrategyUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_token","type":"address"},{"indexed":true,"internalType":"uint256","name":"_minimumReservesRatio","type":"uint256"}],"name":"StrategyTokenUpdated","type":"event"},{"inputs":[],"name":"DELEGATEE","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DELEGATION_REGISTRY","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_bridgeAgent","type":"address"}],"name":"addBridgeAgent","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"bridgeAgents","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_recipient","type":"address"},{"internalType":"address","name":"_localAddress","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"bridgeIn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_recipient","type":"address"},{"internalType":"address[]","name":"_localAddresses","type":"address[]"},{"internalType":"address[]","name":"_underlyingAddresses","type":"address[]"},{"internalType":"uint256[]","name":"_amounts","type":"uint256[]"},{"internalType":"uint256[]","name":"_deposits","type":"uint256[]"}],"name":"bridgeInMultiple","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_depositor","type":"address"},{"internalType":"address","name":"_localAddress","type":"address"},{"internalType":"address","name":"_underlyingAddress","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"uint256","name":"_deposit","type":"uint256"}],"name":"bridgeOut","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_depositor","type":"address"},{"internalType":"address[]","name":"_localAddresses","type":"address[]"},{"internalType":"address[]","name":"_underlyingAddresses","type":"address[]"},{"internalType":"uint256[]","name":"_amounts","type":"uint256[]"},{"internalType":"uint256[]","name":"_deposits","type":"uint256[]"}],"name":"bridgeOutMultiple","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"cancelOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"completeOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"coreBranchRouterAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"getMinimumTokenReserveRatio","outputs":[{"internalType":"uint256","name":"minimumReserveRatio","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"strategy","type":"address"},{"internalType":"address","name":"token","type":"address"}],"name":"getPortStrategyTokenDebt","outputs":[{"internalType":"uint256","name":"debt","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"getStrategyTokenDebt","outputs":[{"internalType":"uint256","name":"debt","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_coreBranchRouter","type":"address"},{"internalType":"address","name":"_bridgeAgentFactory","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"bridgeAgent","type":"address"}],"name":"isBridgeAgent","outputs":[{"internalType":"bool","name":"isActiveBridgeAgent","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"bridgeAgentFactory","type":"address"}],"name":"isBridgeAgentFactory","outputs":[{"internalType":"bool","name":"isActiveBridgeAgentFactory","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"strategy","type":"address"},{"internalType":"address","name":"token","type":"address"}],"name":"isPortStrategy","outputs":[{"internalType":"bool","name":"isActiveStrategy","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"isStrategyToken","outputs":[{"internalType":"bool","name":"allowsStrategies","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"strategy","type":"address"},{"internalType":"address","name":"token","type":"address"}],"name":"lastManaged","outputs":[{"internalType":"uint256","name":"lastManaged","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"manage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"result","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"ownershipHandoverExpiresAt","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"_strategy","type":"address"},{"internalType":"address","name":"_token","type":"address"}],"name":"replenishReserves","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"replenishReserves","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"requestOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"_coreBranchRouter","type":"address"},{"internalType":"address","name":"_coreBranchBridgeAgent","type":"address"}],"name":"setCoreBranchRouter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"strategy","type":"address"},{"internalType":"address","name":"token","type":"address"}],"name":"strategyDailyLimitAmount","outputs":[{"internalType":"uint256","name":"dailyLimitAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"strategy","type":"address"},{"internalType":"address","name":"token","type":"address"}],"name":"strategyDailyLimitRemaining","outputs":[{"internalType":"uint256","name":"dailyLimitRemaining","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"strategy","type":"address"},{"internalType":"address","name":"token","type":"address"}],"name":"strategyReserveRatioManagementLimit","outputs":[{"internalType":"uint256","name":"reserveRatioManagementLimit","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_recipient","type":"address"}],"name":"sweep","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newBridgeAgentFactory","type":"address"}],"name":"toggleBridgeAgentFactory","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_portStrategy","type":"address"},{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint256","name":"_dailyManagementLimit","type":"uint256"},{"internalType":"uint256","name":"_reserveRatioManagementLimit","type":"uint256"}],"name":"togglePortStrategy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint256","name":"_minimumReservesRatio","type":"uint256"}],"name":"toggleStrategyToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"_portStrategy","type":"address"},{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint256","name":"_dailyManagementLimit","type":"uint256"},{"internalType":"uint256","name":"_reserveRatioManagementLimit","type":"uint256"}],"name":"updatePortStrategy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint256","name":"_minimumReservesRatio","type":"uint256"}],"name":"updateStrategyToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_recipient","type":"address"},{"internalType":"address","name":"_underlyingAddress","type":"address"},{"internalType":"uint256","name":"_deposit","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]Contract Creation Code
608060409080825234620001c25760208162002ebf8038038091620000258285620001f9565b833981010312620001c257516001600160a01b03811690819003620001c2576000906001825580156200017e5780638b78c6d81955817f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08180a381516302b8a21d60e01b602082019081527388e07a0457aa113ab910103d9a01217315da1c98602480840191909152825290606081016001600160401b038111828210176200016a5783809392819287525173098c837fef2e146e96ceaf58a10f68fc6326dc4c9382855af150620000f66200021d565b508180855160208101906325ce9a3760e01b8252600481526200011981620001c7565b519082855af1506200012a6200021d565b5083518260208201916353f340a360e11b8352600481526200014c81620001c7565b51925af1506200015b6200021d565b5051612c5e9081620002618239f35b634e487b7160e01b84526041600452602484fd5b825162461bcd60e51b815260206004820152601560248201527f4f776e6572206973207a65726f206164647265737300000000000000000000006044820152606490fd5b600080fd5b604081019081106001600160401b03821117620001e357604052565b634e487b7160e01b600052604160045260246000fd5b601f909101601f19168101906001600160401b03821190821017620001e357604052565b3d156200025b573d906001600160401b038211620001e357604051916200024f601f8201601f191660200184620001f9565b82523d6000602084013e565b60609056fe6080604081815260049182361015610022575b505050361561002057600080fd5b005b600092833560e01c91826301681a62146122cb575081630b0eee3014611f7057816310099f1f14611f2357816314f6032914611eaf5781632569296214611e46578163278c74c914611d2d57816333a8a17d14611cda5781633454ab4414611ba5578163485cc955146119a75781634daadff71461195a57816354d1f13d146118f657816356a43fde1461181357816358e60c071461179f5781635cc2e4d1146116ff5781636d42113914611560578163715018a6146114e15781637d3b10961461146d5781637fd4e5401461137f5781637ff6b65e1461127b5781638038b03814610f805781638da5cb5b14610f0e5781639777a4eb14610e88578163acb2a5a914610e0c578163b0c5a84b14610daa578163ba1ac97314610d48578163c169acda14610cd1578163d3fbcc2e14610c6b578163d9caed1214610c29578163e0cc8c2514610b61578163e99e28c914610aea578163f04e283e14610a20578163f184f03d146109ac578163f1b22855146107c6578163f2fde38b14610711578163f50e850e146106aa578163f72f6a2414610566578163f8f1185b1461029957508063fb6f948d146102335763fee81cf4146101df5780610012565b3461022f5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261022f5760209161021961236b565b9063389a75e1600c525281600c20549051908152f35b5080fd5b503461022f5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261022f5760ff8160209373ffffffffffffffffffffffffffffffffffffffff61028661236b565b1681526002855220541690519015158152f35b9190503461056257807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610562576102d261236b565b91602435916102e460018654146126d9565b60028555821561053c57338552602090600982528086209473ffffffffffffffffffffffffffffffffffffffff8116958660005283528482600020541061051457815193877f70a082310000000000000000000000000000000000000000000000000000000092838752308388015285876024818c5afa9687156104d75782976104e1575b50333b1561022f5784517fd9caed120000000000000000000000000000000000000000000000000000000081523084820190815273ffffffffffffffffffffffffffffffffffffffff90921660208301526040820189905290829082908190606001038183335af180156104d7576104bf575b5050825191825230908201528281602481895afa9081156104b557908591889161047d575b50906104317feef1a28252f7c3266ccea3202ee9693f0834485bc3551310a406e04829c7e2cf95610437936127c3565b146127d0565b33865260098252808620856000528252806000206104568582546127c3565b90558486526006825280862061046d8582546127c3565b9055519283523392a36001815580f35b809250848092503d83116104ae575b61049681836123f4565b810103126104a957518490610431610401565b600080fd5b503d61048c565b82513d89823e3d90fd5b6104c8906123b1565b6104d35787386103dc565b8780fd5b85513d84823e3d90fd5b86809298508193503d831161050d575b6104fb81836123f4565b810103126104a9578890519538610369565b503d6104f1565b8382517fac4314a9000000000000000000000000000000000000000000000000000000008152fd5b517f2c5211c6000000000000000000000000000000000000000000000000000000008152fd5b8280fd5b90503461056257817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105625761059e61236b565b9073ffffffffffffffffffffffffffffffffffffffff908160015416330361068357508116908184526005602052828420805460ff811660001461064a5750505080835260056020528183207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0081541690558060005260076020526127108092600020557f9885ff73a21fcba220892b49f18fa8828a6350b3c2e2658aa47f512ef6a1ac09600080a380f35b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905561068092506024359150612903565b80f35b83517fadf3b019000000000000000000000000000000000000000000000000000000008152fd5b50503461022f5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261022f5760ff8160209373ffffffffffffffffffffffffffffffffffffffff6106fe61236b565b1681526005855220541690519015158152f35b839060207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261022f5761074561236b565b9061074e6126a1565b8160601b156107bb575073ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffffffffffffffffffffffffffffffffffff748739278181547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a35580f35b637448fbae8352601cfd5b9190503461056257807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610562576107ff61236b565b9061080861238e565b9060015473ffffffffffffffffffffffffffffffffffffffff9384821633036109845784169384156109275783169485156108ca575090837fffffffffffffffffffffffff00000000000000000000000000000000000000006108a3949316176001558486526002602052852060017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00825416179055612878565b7f8d87378c36cfd04f9d8d5fe5b131669eee2e0959410f1bd3889c47d7378528978380a380f35b60649060208451917f08c379a0000000000000000000000000000000000000000000000000000000008352820152602060248201527f4e657720427269646765204167656e742061646472657373206973207a65726f6044820152fd5b60648660208551917f08c379a0000000000000000000000000000000000000000000000000000000008352820152601e60248201527f4e657720436f7265526f757465722061646472657373206973207a65726f00006044820152fd5b8583517fadf3b019000000000000000000000000000000000000000000000000000000008152fd5b83833461022f576109bc36612641565b926109cd60018694939454146126d9565b60028555338552600260205260ff8186205416156109f857506109f1939450612a23565b6001815580f35b8590517f541ba3f8000000000000000000000000000000000000000000000000000000008152fd5b8360207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610ae757610a5361236b565b610a5b6126a1565b63389a75e1600c528082526020600c209283544211610adc57508173ffffffffffffffffffffffffffffffffffffffff929355167fffffffffffffffffffffffffffffffffffffffffffffffffffffffff748739278181547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a35580f35b636f5e88188352601cfd5b80fd5b50503461022f57807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261022f57602091610b2561236b565b82610b2e61238e565b9273ffffffffffffffffffffffffffffffffffffffff8093168152600a8652209116600052825280600020549051908152f35b90503461056257817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261056257610b9961236b565b9173ffffffffffffffffffffffffffffffffffffffff80600154163303610c025783168452600560205260ff818520541615610bdc578361068060243585612903565b517fdf412a27000000000000000000000000000000000000000000000000000000008152fd5b50517fadf3b019000000000000000000000000000000000000000000000000000000008152fd5b83833461022f57610c3936612641565b92610c4760018654146126d9565b60028555338552600260205260ff8186205416156109f857506109f193945061277a565b9050346105625760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610562578160209360ff9273ffffffffffffffffffffffffffffffffffffffff610cc061236b565b168252855220541690519015158152f35b50503461022f57807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261022f57602091610d0c61236b565b82610d1561238e565b9273ffffffffffffffffffffffffffffffffffffffff8093168152600d8652209116600052825280600020549051908152f35b50503461022f5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261022f578060209273ffffffffffffffffffffffffffffffffffffffff610d9a61236b565b1681526006845220549051908152f35b50503461022f5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261022f578060209273ffffffffffffffffffffffffffffffffffffffff610dfc61236b565b1681526007845220549051908152f35b50503461022f57807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261022f57602091610e4761236b565b82610e5061238e565b9273ffffffffffffffffffffffffffffffffffffffff809316815260088652209116600052825260ff81600020541690519015158152f35b9050346105625760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610562573591600354831015610ae7575073ffffffffffffffffffffffffffffffffffffffff60209260036000527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b0154169051908152f35b50503461022f57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261022f5760209073ffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffff7487392754915191168152f35b90503461056257817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261056257610fb861236b565b90610fc161238e565b610fce60018654146126d9565b6002855573ffffffffffffffffffffffffffffffffffffffff90818116948587526020926006845281882054948251957f70a082310000000000000000000000000000000000000000000000000000000092838852308389015286886024818d5afa9788156111dd578b98611248575b508761105361104d848361273e565b886129ed565b8082101561123f5703985b1697888b5260098752848b208a8c528752848b2054908115611217578082101561120f5750945b85156111e7578a893b15610ae75785517fd9caed120000000000000000000000000000000000000000000000000000000081523085820190815273ffffffffffffffffffffffffffffffffffffffff90931660208401526040830188905291829081906060010381838d5af180156111dd576111c6575b508461112891898c5260098852858c208b6000528852856000206111218382546127c3565b90556127c3565b888a5260068652838a20558251918252309082015283816024818a5afa9081156111bc57889161118f575b50826104317feef1a28252f7c3266ccea3202ee9693f0834485bc3551310a406e04829c7e2cf96611183936127c3565b51908152a36001815580f35b90508381813d83116111b5575b6111a681836123f4565b810103126104a9575182611153565b503d61119c565b82513d8a823e3d90fd5b611128919a6111d587926123b1565b9a91506110fc565b85513d8d823e3d90fd5b8285517f2c5211c6000000000000000000000000000000000000000000000000000000008152fd5b905094611085565b8386517fac4314a9000000000000000000000000000000000000000000000000000000008152fd5b50508a9861105e565b9097508681813d8311611274575b61126081836123f4565b810103126112705751963861103e565b8a80fd5b503d611256565b83833461022f5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261022f576112b461236b565b903383528360205260ff8184205416156113575773ffffffffffffffffffffffffffffffffffffffff8216808452600260205260ff828520541661132f5783610680949550526002602052832060017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00825416179055612878565b8482517f7c8e4f64000000000000000000000000000000000000000000000000000000008152fd5b8390517fdbf8f2a1000000000000000000000000000000000000000000000000000000008152fd5b83833461022f5761138f366125dd565b9373ffffffffffffffffffffffffffffffffffffffff939193806001541633036114455780841690818852600560205260ff83892054161561141d5783168752600860205281872090875260205260ff8187205416156113f5575061068094955061295a565b8690517f098df0bb000000000000000000000000000000000000000000000000000000008152fd5b8883517fdf412a27000000000000000000000000000000000000000000000000000000008152fd5b8782517fadf3b019000000000000000000000000000000000000000000000000000000008152fd5b50503461022f57807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261022f57806020926114a961236b565b6114b161238e565b73ffffffffffffffffffffffffffffffffffffffff91821683526009865283832091168252845220549051908152f35b83807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610ae7576115136126a1565b807fffffffffffffffffffffffffffffffffffffffffffffffffffffffff748739278181547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a35580f35b8391503461022f57611571366125dd565b73ffffffffffffffffffffffffffffffffffffffff969293969491856001541633036116d7578588169485885260209860088a5285892097821697888a528a5260ff868a20541660001461165457505050505083947f5f21cbad2d364a9cf930db58804980aa729f3e2f1f4cb91bcd8032d44c06b549918386526008825280862085875282528086207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008154169055838652600c825280862085875282528581812055838652600b8252808620858752825261271090818188205551908152a480f35b93989094929691956005825260ff848a205416156116af5788610680999a50526008815282892091895252862060017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0082541617905561295a565b8984517fdf412a27000000000000000000000000000000000000000000000000000000008152fd5b8284517fadf3b019000000000000000000000000000000000000000000000000000000008152fd5b83833461022f5760a07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261022f5761173861236b565b61174061238e565b916044359073ffffffffffffffffffffffffffffffffffffffff8216820361179b5761176f60018654146126d9565b60028555338552600260205260ff8186205416156109f857506109f19394506084359260643592612abe565b8480fd5b50503461022f57807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261022f57806020926117db61236b565b6117e361238e565b73ffffffffffffffffffffffffffffffffffffffff9182168352600c865283832091168252845220549051908152f35b919050346105625760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105625761184d61236b565b73ffffffffffffffffffffffffffffffffffffffff90816001541633036118ce571691828452602052822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0060ff808316151691161790557fa64bc674084fdd52a9cdb6aca0e184c83739450e349016391528abafd6097c038280a280f35b5050517fadf3b019000000000000000000000000000000000000000000000000000000008152fd5b83807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610ae75763389a75e1600c52338152806020600c2055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c928280a280f35b50503461022f57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261022f576020905173098c837fef2e146e96ceaf58a10f68fc6326dc4c8152f35b90503461056257817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610562576119df61236b565b6119e761238e565b906119f06126a1565b73ffffffffffffffffffffffffffffffffffffffff809116918215611b485716908115611ac557611a1f6126a1565b847fffffffffffffffffffffffffffffffffffffffffffffffffffffffff748739278181547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3557fffffffffffffffffffffffff000000000000000000000000000000000000000060015416176001558352602052812060017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0082541617905580f35b60848360208651917f08c379a0000000000000000000000000000000000000000000000000000000008352820152602260248201527f4272696467654167656e74466163746f7279206973207a65726f20616464726560448201527f73730000000000000000000000000000000000000000000000000000000000006064820152fd5b60648460208751917f08c379a0000000000000000000000000000000000000000000000000000000008352820152602060248201527f436f72654272616e6368526f75746572206973207a65726f20616464726573736044820152fd5b90503461056257611bb536612526565b939294600196611bc7888a54146126d9565b60028955338952600260205260ff818a20541615611cb457505084519486885b878110611bf5578982815580f35b611c14611c028286612835565b51611c0d838a612835565b51906127c3565b611c6c575b611c238188612835565b51611c31575b018790611be7565b611c6773ffffffffffffffffffffffffffffffffffffffff611c538388612835565b511687611c60848b612835565b519161277a565b611c29565b611caf73ffffffffffffffffffffffffffffffffffffffff611c8e8386612835565b5116611c9a8387612835565b51611ca5848b612835565b5190039088612a23565b611c19565b517f541ba3f8000000000000000000000000000000000000000000000000000000008152fd5b50503461022f57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261022f5760209073ffffffffffffffffffffffffffffffffffffffff600154169051908152f35b9190503461056257611d3e36612526565b92600192919296611d51888a54146126d9565b60028955338952600260205260ff878a20541615611e1f5781519660ff8811611df95783518803611df9578351855103611df9578451865103611df957505086885b878110611da1578982815580f35b611df173ffffffffffffffffffffffffffffffffffffffff80611dc48487612835565b511690611dd18488612835565b5116611ddd8489612835565b5190611de9858b612835565b51928b612abe565b018790611d93565b517f744bba7b000000000000000000000000000000000000000000000000000000008152fd5b86517f541ba3f8000000000000000000000000000000000000000000000000000000008152fd5b83807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610ae75763389a75e1600c523381526202a30042016020600c2055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d8280a280f35b50503461022f57807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261022f5780602092611eeb61236b565b611ef361238e565b73ffffffffffffffffffffffffffffffffffffffff9182168352600b865283832091168252845220549051908152f35b50503461022f57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261022f57602090517388e07a0457aa113ab910103d9a01217315da1c988152f35b83833461022f57807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261022f57611fa861236b565b9260243590611fba60018554146126d9565b6002845573ffffffffffffffffffffffffffffffffffffffff8516928385526020916005835260ff8287205416156122a45733865260088352818620858752835260ff82872054161561227d578486526006835281862054338752600984528287208688528452828720549183517f70a08231000000000000000000000000000000000000000000000000000000008152308282015285816024818b5afa908115612273578991612242575b50612071838261273e565b9061208561207f838d6129ed565b8961273e565b1161221a57612094848861273e565b90338a52600b8752858a20898b528752858a205490612710918203908282116121ee57906120c1916129da565b04106121c7575093869761216c826121869461215c827fa303eceafd63708a6d2c8aed661bd1a5dfb7e2a7035703af663303c20fdea52e9a8a8a8e9f849f338252600d845282822081835284526121468684842054338552600a875285852084865287526121398686205462015180918291426127c3565b1015612194575b506127c3565b93338352600d815283832091835252205561273e565b8b8d5260068952878d205561273e565b338a5260098652848a20898b528652848a2055339061277a565b519283523392a36001815580f35b9050338552600c875285852084865287528585205490338652600a88528686208587528852804204028686205538612140565b83517f85aa8cff000000000000000000000000000000000000000000000000000000008152fd5b60248c6011877f4e487b7100000000000000000000000000000000000000000000000000000000835252fd5b5083517f945e9268000000000000000000000000000000000000000000000000000000008152fd5b90508581813d831161226c575b61225981836123f4565b8101031261226857518a612066565b8880fd5b503d61224f565b85513d8b823e3d90fd5b90517f098df0bb000000000000000000000000000000000000000000000000000000008152fd5b90517fdf412a27000000000000000000000000000000000000000000000000000000008152fd5b849250346105625760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105625761230561236b565b9073ffffffffffffffffffffffffffffffffffffffff600154163303612344575082908180389247905af115612339575080f35b63b12d13eb8252601cfd5b90507fadf3b019000000000000000000000000000000000000000000000000000000008152fd5b6004359073ffffffffffffffffffffffffffffffffffffffff821682036104a957565b6024359073ffffffffffffffffffffffffffffffffffffffff821682036104a957565b67ffffffffffffffff81116123c557604052565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff8211176123c557604052565b67ffffffffffffffff81116123c55760051b60200190565b81601f820112156104a95780359161246483612435565b9261247260405194856123f4565b808452602092838086019260051b8201019283116104a9578301905b82821061249c575050505090565b813573ffffffffffffffffffffffffffffffffffffffff811681036104a957815290830190830161248e565b81601f820112156104a9578035916124df83612435565b926124ed60405194856123f4565b808452602092838086019260051b8201019283116104a9578301905b828210612517575050505090565b81358152908301908301612509565b9060a07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc8301126104a95760043573ffffffffffffffffffffffffffffffffffffffff811681036104a9579167ffffffffffffffff906024358281116104a957816125939160040161244d565b926044358381116104a957826125ab9160040161244d565b926064358181116104a957836125c3916004016124c8565b926084359182116104a9576125da916004016124c8565b90565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60809101126104a95773ffffffffffffffffffffffffffffffffffffffff9060043582811681036104a9579160243590811681036104a957906044359060643590565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60609101126104a95773ffffffffffffffffffffffffffffffffffffffff9060043582811681036104a9579160243590811681036104a9579060443590565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffff748739275433036126cb57565b6382b429006000526004601cfd5b156126e057565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f5245454e5452414e4359000000000000000000000000000000000000000000006044820152fd5b9190820180921161274b57565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60109260209260145260345260446000938480936fa9059cbb00000000000000000000000082525af13d1560018351141716156127b657603452565b6390b8ec1890526004601cfd5b9190820391821161274b57565b156127d757565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f506f7274205374726174656779205769746864726177204661696c65640000006044820152fd5b80518210156128495760209160051b010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600354680100000000000000008110156123c55760018101806003558110156128495773ffffffffffffffffffffffffffffffffffffffff9060036000527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b0191167fffffffffffffffffffffffff0000000000000000000000000000000000000000825416179055565b73ffffffffffffffffffffffffffffffffffffffff9061292283612c14565b16806000526007602052816040600020557f9885ff73a21fcba220892b49f18fa8828a6350b3c2e2658aa47f512ef6a1ac09600080a3565b90919260207f5f21cbad2d364a9cf930db58804980aa729f3e2f1f4cb91bcd8032d44c06b5499161298a81612c14565b80604073ffffffffffffffffffffffffffffffffffffffff80961695600090878252600c865282822098169788825285528882822055868152600b855281812088825285522055604051908152a4565b8181029291811591840414171561274b57565b6127109173ffffffffffffffffffffffffffffffffffffffff612a1f92166000526007602052604060002054906129da565b0490565b9073ffffffffffffffffffffffffffffffffffffffff1691823b156104a9576040517f40c10f1900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9290921660048301526024820152906000908290604490829084905af18015612ab257612aa75750565b612ab0906123b1565b565b6040513d6000823e3d90fd5b919092612acb85826127c3565b93600094612b6e575b505083612ae2575b50505050565b803b15612b44576064601c848093602095604051986060523060405260601b602c526f23b872dd000000000000000000000000600c525af13d156001835114171615612b375760605260405238808080612adc565b637939f42490526004601cfd5b60046040517fe9a1cca5000000000000000000000000000000000000000000000000000000008152fd5b73ffffffffffffffffffffffffffffffffffffffff1690813b1561179b576040517f9dc29fac00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff851660048201529086900360248201529084908290604490829084905af18015612c0957612bf6575b80612ad4565b612c02909391936123b1565b9138612bf0565b6040513d86823e3d90fd5b6127108111612c2757611b5811612c2757565b60046040517f9a375315000000000000000000000000000000000000000000000000000000008152fdfea164736f6c6343000813000a0000000000000000000000009f1a52db8b0465cf189206b3a36303cd415a199a
Deployed Bytecode
0x6080604081815260049182361015610022575b505050361561002057600080fd5b005b600092833560e01c91826301681a62146122cb575081630b0eee3014611f7057816310099f1f14611f2357816314f6032914611eaf5781632569296214611e46578163278c74c914611d2d57816333a8a17d14611cda5781633454ab4414611ba5578163485cc955146119a75781634daadff71461195a57816354d1f13d146118f657816356a43fde1461181357816358e60c071461179f5781635cc2e4d1146116ff5781636d42113914611560578163715018a6146114e15781637d3b10961461146d5781637fd4e5401461137f5781637ff6b65e1461127b5781638038b03814610f805781638da5cb5b14610f0e5781639777a4eb14610e88578163acb2a5a914610e0c578163b0c5a84b14610daa578163ba1ac97314610d48578163c169acda14610cd1578163d3fbcc2e14610c6b578163d9caed1214610c29578163e0cc8c2514610b61578163e99e28c914610aea578163f04e283e14610a20578163f184f03d146109ac578163f1b22855146107c6578163f2fde38b14610711578163f50e850e146106aa578163f72f6a2414610566578163f8f1185b1461029957508063fb6f948d146102335763fee81cf4146101df5780610012565b3461022f5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261022f5760209161021961236b565b9063389a75e1600c525281600c20549051908152f35b5080fd5b503461022f5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261022f5760ff8160209373ffffffffffffffffffffffffffffffffffffffff61028661236b565b1681526002855220541690519015158152f35b9190503461056257807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610562576102d261236b565b91602435916102e460018654146126d9565b60028555821561053c57338552602090600982528086209473ffffffffffffffffffffffffffffffffffffffff8116958660005283528482600020541061051457815193877f70a082310000000000000000000000000000000000000000000000000000000092838752308388015285876024818c5afa9687156104d75782976104e1575b50333b1561022f5784517fd9caed120000000000000000000000000000000000000000000000000000000081523084820190815273ffffffffffffffffffffffffffffffffffffffff90921660208301526040820189905290829082908190606001038183335af180156104d7576104bf575b5050825191825230908201528281602481895afa9081156104b557908591889161047d575b50906104317feef1a28252f7c3266ccea3202ee9693f0834485bc3551310a406e04829c7e2cf95610437936127c3565b146127d0565b33865260098252808620856000528252806000206104568582546127c3565b90558486526006825280862061046d8582546127c3565b9055519283523392a36001815580f35b809250848092503d83116104ae575b61049681836123f4565b810103126104a957518490610431610401565b600080fd5b503d61048c565b82513d89823e3d90fd5b6104c8906123b1565b6104d35787386103dc565b8780fd5b85513d84823e3d90fd5b86809298508193503d831161050d575b6104fb81836123f4565b810103126104a9578890519538610369565b503d6104f1565b8382517fac4314a9000000000000000000000000000000000000000000000000000000008152fd5b517f2c5211c6000000000000000000000000000000000000000000000000000000008152fd5b8280fd5b90503461056257817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105625761059e61236b565b9073ffffffffffffffffffffffffffffffffffffffff908160015416330361068357508116908184526005602052828420805460ff811660001461064a5750505080835260056020528183207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0081541690558060005260076020526127108092600020557f9885ff73a21fcba220892b49f18fa8828a6350b3c2e2658aa47f512ef6a1ac09600080a380f35b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905561068092506024359150612903565b80f35b83517fadf3b019000000000000000000000000000000000000000000000000000000008152fd5b50503461022f5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261022f5760ff8160209373ffffffffffffffffffffffffffffffffffffffff6106fe61236b565b1681526005855220541690519015158152f35b839060207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261022f5761074561236b565b9061074e6126a1565b8160601b156107bb575073ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffffffffffffffffffffffffffffffffffff748739278181547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a35580f35b637448fbae8352601cfd5b9190503461056257807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610562576107ff61236b565b9061080861238e565b9060015473ffffffffffffffffffffffffffffffffffffffff9384821633036109845784169384156109275783169485156108ca575090837fffffffffffffffffffffffff00000000000000000000000000000000000000006108a3949316176001558486526002602052852060017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00825416179055612878565b7f8d87378c36cfd04f9d8d5fe5b131669eee2e0959410f1bd3889c47d7378528978380a380f35b60649060208451917f08c379a0000000000000000000000000000000000000000000000000000000008352820152602060248201527f4e657720427269646765204167656e742061646472657373206973207a65726f6044820152fd5b60648660208551917f08c379a0000000000000000000000000000000000000000000000000000000008352820152601e60248201527f4e657720436f7265526f757465722061646472657373206973207a65726f00006044820152fd5b8583517fadf3b019000000000000000000000000000000000000000000000000000000008152fd5b83833461022f576109bc36612641565b926109cd60018694939454146126d9565b60028555338552600260205260ff8186205416156109f857506109f1939450612a23565b6001815580f35b8590517f541ba3f8000000000000000000000000000000000000000000000000000000008152fd5b8360207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610ae757610a5361236b565b610a5b6126a1565b63389a75e1600c528082526020600c209283544211610adc57508173ffffffffffffffffffffffffffffffffffffffff929355167fffffffffffffffffffffffffffffffffffffffffffffffffffffffff748739278181547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a35580f35b636f5e88188352601cfd5b80fd5b50503461022f57807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261022f57602091610b2561236b565b82610b2e61238e565b9273ffffffffffffffffffffffffffffffffffffffff8093168152600a8652209116600052825280600020549051908152f35b90503461056257817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261056257610b9961236b565b9173ffffffffffffffffffffffffffffffffffffffff80600154163303610c025783168452600560205260ff818520541615610bdc578361068060243585612903565b517fdf412a27000000000000000000000000000000000000000000000000000000008152fd5b50517fadf3b019000000000000000000000000000000000000000000000000000000008152fd5b83833461022f57610c3936612641565b92610c4760018654146126d9565b60028555338552600260205260ff8186205416156109f857506109f193945061277a565b9050346105625760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610562578160209360ff9273ffffffffffffffffffffffffffffffffffffffff610cc061236b565b168252855220541690519015158152f35b50503461022f57807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261022f57602091610d0c61236b565b82610d1561238e565b9273ffffffffffffffffffffffffffffffffffffffff8093168152600d8652209116600052825280600020549051908152f35b50503461022f5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261022f578060209273ffffffffffffffffffffffffffffffffffffffff610d9a61236b565b1681526006845220549051908152f35b50503461022f5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261022f578060209273ffffffffffffffffffffffffffffffffffffffff610dfc61236b565b1681526007845220549051908152f35b50503461022f57807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261022f57602091610e4761236b565b82610e5061238e565b9273ffffffffffffffffffffffffffffffffffffffff809316815260088652209116600052825260ff81600020541690519015158152f35b9050346105625760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610562573591600354831015610ae7575073ffffffffffffffffffffffffffffffffffffffff60209260036000527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b0154169051908152f35b50503461022f57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261022f5760209073ffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffff7487392754915191168152f35b90503461056257817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261056257610fb861236b565b90610fc161238e565b610fce60018654146126d9565b6002855573ffffffffffffffffffffffffffffffffffffffff90818116948587526020926006845281882054948251957f70a082310000000000000000000000000000000000000000000000000000000092838852308389015286886024818d5afa9788156111dd578b98611248575b508761105361104d848361273e565b886129ed565b8082101561123f5703985b1697888b5260098752848b208a8c528752848b2054908115611217578082101561120f5750945b85156111e7578a893b15610ae75785517fd9caed120000000000000000000000000000000000000000000000000000000081523085820190815273ffffffffffffffffffffffffffffffffffffffff90931660208401526040830188905291829081906060010381838d5af180156111dd576111c6575b508461112891898c5260098852858c208b6000528852856000206111218382546127c3565b90556127c3565b888a5260068652838a20558251918252309082015283816024818a5afa9081156111bc57889161118f575b50826104317feef1a28252f7c3266ccea3202ee9693f0834485bc3551310a406e04829c7e2cf96611183936127c3565b51908152a36001815580f35b90508381813d83116111b5575b6111a681836123f4565b810103126104a9575182611153565b503d61119c565b82513d8a823e3d90fd5b611128919a6111d587926123b1565b9a91506110fc565b85513d8d823e3d90fd5b8285517f2c5211c6000000000000000000000000000000000000000000000000000000008152fd5b905094611085565b8386517fac4314a9000000000000000000000000000000000000000000000000000000008152fd5b50508a9861105e565b9097508681813d8311611274575b61126081836123f4565b810103126112705751963861103e565b8a80fd5b503d611256565b83833461022f5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261022f576112b461236b565b903383528360205260ff8184205416156113575773ffffffffffffffffffffffffffffffffffffffff8216808452600260205260ff828520541661132f5783610680949550526002602052832060017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00825416179055612878565b8482517f7c8e4f64000000000000000000000000000000000000000000000000000000008152fd5b8390517fdbf8f2a1000000000000000000000000000000000000000000000000000000008152fd5b83833461022f5761138f366125dd565b9373ffffffffffffffffffffffffffffffffffffffff939193806001541633036114455780841690818852600560205260ff83892054161561141d5783168752600860205281872090875260205260ff8187205416156113f5575061068094955061295a565b8690517f098df0bb000000000000000000000000000000000000000000000000000000008152fd5b8883517fdf412a27000000000000000000000000000000000000000000000000000000008152fd5b8782517fadf3b019000000000000000000000000000000000000000000000000000000008152fd5b50503461022f57807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261022f57806020926114a961236b565b6114b161238e565b73ffffffffffffffffffffffffffffffffffffffff91821683526009865283832091168252845220549051908152f35b83807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610ae7576115136126a1565b807fffffffffffffffffffffffffffffffffffffffffffffffffffffffff748739278181547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a35580f35b8391503461022f57611571366125dd565b73ffffffffffffffffffffffffffffffffffffffff969293969491856001541633036116d7578588169485885260209860088a5285892097821697888a528a5260ff868a20541660001461165457505050505083947f5f21cbad2d364a9cf930db58804980aa729f3e2f1f4cb91bcd8032d44c06b549918386526008825280862085875282528086207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008154169055838652600c825280862085875282528581812055838652600b8252808620858752825261271090818188205551908152a480f35b93989094929691956005825260ff848a205416156116af5788610680999a50526008815282892091895252862060017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0082541617905561295a565b8984517fdf412a27000000000000000000000000000000000000000000000000000000008152fd5b8284517fadf3b019000000000000000000000000000000000000000000000000000000008152fd5b83833461022f5760a07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261022f5761173861236b565b61174061238e565b916044359073ffffffffffffffffffffffffffffffffffffffff8216820361179b5761176f60018654146126d9565b60028555338552600260205260ff8186205416156109f857506109f19394506084359260643592612abe565b8480fd5b50503461022f57807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261022f57806020926117db61236b565b6117e361238e565b73ffffffffffffffffffffffffffffffffffffffff9182168352600c865283832091168252845220549051908152f35b919050346105625760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105625761184d61236b565b73ffffffffffffffffffffffffffffffffffffffff90816001541633036118ce571691828452602052822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0060ff808316151691161790557fa64bc674084fdd52a9cdb6aca0e184c83739450e349016391528abafd6097c038280a280f35b5050517fadf3b019000000000000000000000000000000000000000000000000000000008152fd5b83807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610ae75763389a75e1600c52338152806020600c2055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c928280a280f35b50503461022f57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261022f576020905173098c837fef2e146e96ceaf58a10f68fc6326dc4c8152f35b90503461056257817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610562576119df61236b565b6119e761238e565b906119f06126a1565b73ffffffffffffffffffffffffffffffffffffffff809116918215611b485716908115611ac557611a1f6126a1565b847fffffffffffffffffffffffffffffffffffffffffffffffffffffffff748739278181547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3557fffffffffffffffffffffffff000000000000000000000000000000000000000060015416176001558352602052812060017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0082541617905580f35b60848360208651917f08c379a0000000000000000000000000000000000000000000000000000000008352820152602260248201527f4272696467654167656e74466163746f7279206973207a65726f20616464726560448201527f73730000000000000000000000000000000000000000000000000000000000006064820152fd5b60648460208751917f08c379a0000000000000000000000000000000000000000000000000000000008352820152602060248201527f436f72654272616e6368526f75746572206973207a65726f20616464726573736044820152fd5b90503461056257611bb536612526565b939294600196611bc7888a54146126d9565b60028955338952600260205260ff818a20541615611cb457505084519486885b878110611bf5578982815580f35b611c14611c028286612835565b51611c0d838a612835565b51906127c3565b611c6c575b611c238188612835565b51611c31575b018790611be7565b611c6773ffffffffffffffffffffffffffffffffffffffff611c538388612835565b511687611c60848b612835565b519161277a565b611c29565b611caf73ffffffffffffffffffffffffffffffffffffffff611c8e8386612835565b5116611c9a8387612835565b51611ca5848b612835565b5190039088612a23565b611c19565b517f541ba3f8000000000000000000000000000000000000000000000000000000008152fd5b50503461022f57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261022f5760209073ffffffffffffffffffffffffffffffffffffffff600154169051908152f35b9190503461056257611d3e36612526565b92600192919296611d51888a54146126d9565b60028955338952600260205260ff878a20541615611e1f5781519660ff8811611df95783518803611df9578351855103611df9578451865103611df957505086885b878110611da1578982815580f35b611df173ffffffffffffffffffffffffffffffffffffffff80611dc48487612835565b511690611dd18488612835565b5116611ddd8489612835565b5190611de9858b612835565b51928b612abe565b018790611d93565b517f744bba7b000000000000000000000000000000000000000000000000000000008152fd5b86517f541ba3f8000000000000000000000000000000000000000000000000000000008152fd5b83807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610ae75763389a75e1600c523381526202a30042016020600c2055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d8280a280f35b50503461022f57807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261022f5780602092611eeb61236b565b611ef361238e565b73ffffffffffffffffffffffffffffffffffffffff9182168352600b865283832091168252845220549051908152f35b50503461022f57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261022f57602090517388e07a0457aa113ab910103d9a01217315da1c988152f35b83833461022f57807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261022f57611fa861236b565b9260243590611fba60018554146126d9565b6002845573ffffffffffffffffffffffffffffffffffffffff8516928385526020916005835260ff8287205416156122a45733865260088352818620858752835260ff82872054161561227d578486526006835281862054338752600984528287208688528452828720549183517f70a08231000000000000000000000000000000000000000000000000000000008152308282015285816024818b5afa908115612273578991612242575b50612071838261273e565b9061208561207f838d6129ed565b8961273e565b1161221a57612094848861273e565b90338a52600b8752858a20898b528752858a205490612710918203908282116121ee57906120c1916129da565b04106121c7575093869761216c826121869461215c827fa303eceafd63708a6d2c8aed661bd1a5dfb7e2a7035703af663303c20fdea52e9a8a8a8e9f849f338252600d845282822081835284526121468684842054338552600a875285852084865287526121398686205462015180918291426127c3565b1015612194575b506127c3565b93338352600d815283832091835252205561273e565b8b8d5260068952878d205561273e565b338a5260098652848a20898b528652848a2055339061277a565b519283523392a36001815580f35b9050338552600c875285852084865287528585205490338652600a88528686208587528852804204028686205538612140565b83517f85aa8cff000000000000000000000000000000000000000000000000000000008152fd5b60248c6011877f4e487b7100000000000000000000000000000000000000000000000000000000835252fd5b5083517f945e9268000000000000000000000000000000000000000000000000000000008152fd5b90508581813d831161226c575b61225981836123f4565b8101031261226857518a612066565b8880fd5b503d61224f565b85513d8b823e3d90fd5b90517f098df0bb000000000000000000000000000000000000000000000000000000008152fd5b90517fdf412a27000000000000000000000000000000000000000000000000000000008152fd5b849250346105625760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105625761230561236b565b9073ffffffffffffffffffffffffffffffffffffffff600154163303612344575082908180389247905af115612339575080f35b63b12d13eb8252601cfd5b90507fadf3b019000000000000000000000000000000000000000000000000000000008152fd5b6004359073ffffffffffffffffffffffffffffffffffffffff821682036104a957565b6024359073ffffffffffffffffffffffffffffffffffffffff821682036104a957565b67ffffffffffffffff81116123c557604052565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff8211176123c557604052565b67ffffffffffffffff81116123c55760051b60200190565b81601f820112156104a95780359161246483612435565b9261247260405194856123f4565b808452602092838086019260051b8201019283116104a9578301905b82821061249c575050505090565b813573ffffffffffffffffffffffffffffffffffffffff811681036104a957815290830190830161248e565b81601f820112156104a9578035916124df83612435565b926124ed60405194856123f4565b808452602092838086019260051b8201019283116104a9578301905b828210612517575050505090565b81358152908301908301612509565b9060a07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc8301126104a95760043573ffffffffffffffffffffffffffffffffffffffff811681036104a9579167ffffffffffffffff906024358281116104a957816125939160040161244d565b926044358381116104a957826125ab9160040161244d565b926064358181116104a957836125c3916004016124c8565b926084359182116104a9576125da916004016124c8565b90565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60809101126104a95773ffffffffffffffffffffffffffffffffffffffff9060043582811681036104a9579160243590811681036104a957906044359060643590565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60609101126104a95773ffffffffffffffffffffffffffffffffffffffff9060043582811681036104a9579160243590811681036104a9579060443590565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffff748739275433036126cb57565b6382b429006000526004601cfd5b156126e057565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f5245454e5452414e4359000000000000000000000000000000000000000000006044820152fd5b9190820180921161274b57565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60109260209260145260345260446000938480936fa9059cbb00000000000000000000000082525af13d1560018351141716156127b657603452565b6390b8ec1890526004601cfd5b9190820391821161274b57565b156127d757565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f506f7274205374726174656779205769746864726177204661696c65640000006044820152fd5b80518210156128495760209160051b010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600354680100000000000000008110156123c55760018101806003558110156128495773ffffffffffffffffffffffffffffffffffffffff9060036000527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b0191167fffffffffffffffffffffffff0000000000000000000000000000000000000000825416179055565b73ffffffffffffffffffffffffffffffffffffffff9061292283612c14565b16806000526007602052816040600020557f9885ff73a21fcba220892b49f18fa8828a6350b3c2e2658aa47f512ef6a1ac09600080a3565b90919260207f5f21cbad2d364a9cf930db58804980aa729f3e2f1f4cb91bcd8032d44c06b5499161298a81612c14565b80604073ffffffffffffffffffffffffffffffffffffffff80961695600090878252600c865282822098169788825285528882822055868152600b855281812088825285522055604051908152a4565b8181029291811591840414171561274b57565b6127109173ffffffffffffffffffffffffffffffffffffffff612a1f92166000526007602052604060002054906129da565b0490565b9073ffffffffffffffffffffffffffffffffffffffff1691823b156104a9576040517f40c10f1900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9290921660048301526024820152906000908290604490829084905af18015612ab257612aa75750565b612ab0906123b1565b565b6040513d6000823e3d90fd5b919092612acb85826127c3565b93600094612b6e575b505083612ae2575b50505050565b803b15612b44576064601c848093602095604051986060523060405260601b602c526f23b872dd000000000000000000000000600c525af13d156001835114171615612b375760605260405238808080612adc565b637939f42490526004601cfd5b60046040517fe9a1cca5000000000000000000000000000000000000000000000000000000008152fd5b73ffffffffffffffffffffffffffffffffffffffff1690813b1561179b576040517f9dc29fac00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff851660048201529086900360248201529084908290604490829084905af18015612c0957612bf6575b80612ad4565b612c02909391936123b1565b9138612bf0565b6040513d86823e3d90fd5b6127108111612c2757611b5811612c2757565b60046040517f9a375315000000000000000000000000000000000000000000000000000000008152fdfea164736f6c6343000813000a
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000009f1a52db8b0465cf189206b3a36303cd415a199a
-----Decoded View---------------
Arg [0] : _owner (address): 0x9f1A52dB8b0465cf189206B3A36303cd415A199a
-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 0000000000000000000000009f1a52db8b0465cf189206b3a36303cd415a199a
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in FRAX
0
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.