Source Code
Latest 10 from a total of 10 transactions
| Transaction Hash |
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
| Add Local Token | 63911184 | 445 days ago | IN | 10 POL | 0.09111246 | ||||
| Add Local Token | 62974753 | 469 days ago | IN | 69 POL | 0.04551099 | ||||
| Add Local Token | 62973296 | 469 days ago | IN | 10 POL | 0.04137363 | ||||
| Add Local Token | 62973293 | 469 days ago | IN | 10 POL | 0.04137363 | ||||
| Add Local Token | 62973291 | 469 days ago | IN | 10 POL | 0.04137363 | ||||
| Add Local Token | 62973287 | 469 days ago | IN | 10 POL | 0.04137363 | ||||
| Add Local Token | 62973285 | 469 days ago | IN | 10 POL | 0.04137363 | ||||
| Add Local Token | 62973281 | 469 days ago | IN | 10 POL | 0.04147016 | ||||
| Add Local Token | 62973239 | 469 days ago | IN | 10 POL | 0.04213904 | ||||
| Initialize | 62633697 | 477 days ago | IN | 0 POL | 0.00236612 |
Latest 13 internal transactions
| Parent Transaction Hash | Block | From | To | |||
|---|---|---|---|---|---|---|
| 63911184 | 445 days ago | 10 POL | ||||
| 62974753 | 469 days ago | 69 POL | ||||
| 62973296 | 469 days ago | 10 POL | ||||
| 62973293 | 469 days ago | 10 POL | ||||
| 62973291 | 469 days ago | 10 POL | ||||
| 62973287 | 469 days ago | 10 POL | ||||
| 62973285 | 469 days ago | 10 POL | ||||
| 62973281 | 469 days ago | 10 POL | ||||
| 62973239 | 469 days ago | 10 POL | ||||
| 62633756 | 477 days ago | 10 POL | ||||
| 62633756 | 477 days ago | 10 POL | ||||
| 62633746 | 477 days ago | 10 POL | ||||
| 62633746 | 477 days ago | 10 POL |
Cross-Chain Transactions
Loading...
Loading
Contract Name:
CoreBranchRouter
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 {ERC20} from "lib/solmate/src/tokens/ERC20.sol";
import {IBranchPort as IPort} from "./interfaces/IBranchPort.sol";
import {IBranchBridgeAgent as IBridgeAgent, GasParams} from "./interfaces/IBranchBridgeAgent.sol";
import {IBranchBridgeAgentFactory as IBridgeAgentFactory} from "./interfaces/IBranchBridgeAgentFactory.sol";
import {IBranchRouter} from "./interfaces/IBranchRouter.sol";
import {ICoreBranchRouter} from "./interfaces/ICoreBranchRouter.sol";
import {IERC20hTokenBranchFactory as ITokenFactory} from "./interfaces/IERC20hTokenBranchFactory.sol";
import {BaseBranchRouter, DepositInput, DepositMultipleInput} from "./BaseBranchRouter.sol";
import {ERC20hToken} from "./token/ERC20hToken.sol";
/// @title Core Branch Router Contract
/// @author MaiaDAO
contract CoreBranchRouter is ICoreBranchRouter, BaseBranchRouter {
/// @notice hToken Factory Address.
address public immutable hTokenFactoryAddress;
/*///////////////////////////////////////////////////////////////
CONSTRUCTOR
///////////////////////////////////////////////////////////////*/
/**
* @notice Constructor for Core Branch Router.
* @param _hTokenFactoryAddress Branch hToken Factory Address.
*/
constructor(address _hTokenFactoryAddress) BaseBranchRouter() {
hTokenFactoryAddress = _hTokenFactoryAddress;
}
/*///////////////////////////////////////////////////////////////
EXTERNAL FUNCTIONS
///////////////////////////////////////////////////////////////*/
/// @inheritdoc IBranchRouter
function callOut(bytes calldata _params, GasParams calldata _gParams) external payable override {
revert UnrecognizedFunctionId();
}
/// @inheritdoc IBranchRouter
function callOutAndBridge(bytes calldata _params, DepositInput calldata _dParams, GasParams calldata _gParams)
external
payable
override
{
revert UnrecognizedFunctionId();
}
/// @inheritdoc IBranchRouter
function callOutAndBridgeMultiple(
bytes calldata _params,
DepositMultipleInput calldata _dParams,
GasParams calldata _gParams
) external payable override {
revert UnrecognizedFunctionId();
}
/// @inheritdoc IBranchRouter
function retryDeposit(uint32 _depositNonce, bytes calldata _params, GasParams calldata _gParams)
external
payable
override
{
revert UnrecognizedFunctionId();
}
/*///////////////////////////////////////////////////////////////
TOKEN MANAGEMENT EXTERNAL FUNCTIONS
///////////////////////////////////////////////////////////////*/
/**
* @notice This function is used to add a global token to a branch.
* @param _globalAddress Address of the token to be added.
* @param _dstChainId Chain Id of the chain to which the deposit is being added.
* @param _gParams Gas parameters for remote execution.
*/
function addGlobalToken(address _globalAddress, uint256 _dstChainId, GasParams[3] calldata _gParams)
external
payable
{
// Encode Call Data
bytes memory params = abi.encode(msg.sender, _globalAddress, _dstChainId, [_gParams[1], _gParams[2]]);
// Pack FuncId
bytes memory payload = abi.encodePacked(bytes1(0x01), params);
// Send Cross-Chain request (System Response/Request)
IBridgeAgent(localBridgeAgentAddress).callOut{value: msg.value}(payable(msg.sender), payload, _gParams[0]);
}
/**
* @notice This function is used to add a local token to the system.
* @param _underlyingAddress Address of the underlying token to be added.
* @param _gParams Gas parameters for remote execution.
*/
function addLocalToken(address _underlyingAddress, GasParams calldata _gParams) external payable virtual {
//Get Token Info
uint8 decimals = ERC20(_underlyingAddress).decimals();
//Create Token
ERC20hToken newToken = ITokenFactory(hTokenFactoryAddress).createToken(
ERC20(_underlyingAddress).name(), ERC20(_underlyingAddress).symbol(), decimals
);
//Encode Data
bytes memory params = abi.encode(_underlyingAddress, newToken, newToken.name(), newToken.symbol(), decimals);
// Pack FuncId
bytes memory payload = abi.encodePacked(bytes1(0x02), params);
//Send Cross-Chain request (System Response/Request)
IBridgeAgent(localBridgeAgentAddress).callOut{value: msg.value}(payable(msg.sender), payload, _gParams);
}
/*///////////////////////////////////////////////////////////////
LAYERZERO EXTERNAL FUNCTIONS
///////////////////////////////////////////////////////////////*/
/// @inheritdoc IBranchRouter
function executeNoSettlement(bytes calldata _params) external payable virtual override requiresAgentExecutor {
/// _receiveAddGlobalToken
if (_params[0] == 0x01) {
(
address globalAddress,
string memory name,
string memory symbol,
uint8 decimals,
address refundee,
GasParams memory gParams
) = abi.decode(_params[1:], (address, string, string, uint8, address, GasParams));
_receiveAddGlobalToken(globalAddress, name, symbol, decimals, refundee, gParams);
/// _receiveAddBridgeAgent
} else if (_params[0] == 0x02) {
(
address newBranchRouter,
address branchBridgeAgentFactory,
address rootBridgeAgent,
address rootBridgeAgentFactory,
address refundee,
GasParams memory gParams
) = abi.decode(_params[1:], (address, address, address, address, address, GasParams));
_receiveAddBridgeAgent(
newBranchRouter, branchBridgeAgentFactory, rootBridgeAgent, rootBridgeAgentFactory, refundee, gParams
);
/// _toggleBranchBridgeAgentFactory
} else if (_params[0] == 0x03) {
(address bridgeAgentFactoryAddress) = abi.decode(_params[1:], (address));
_toggleBranchBridgeAgentFactory(bridgeAgentFactoryAddress);
/// _toggleStrategyToken
} else if (_params[0] == 0x04) {
(address underlyingToken, uint256 minimumReservesRatio) = abi.decode(_params[1:], (address, uint256));
_toggleStrategyToken(underlyingToken, minimumReservesRatio);
/// _updateStrategyToken
} else if (_params[0] == 0x05) {
(address underlyingToken, uint256 minimumReservesRatio) = abi.decode(_params[1:], (address, uint256));
_updateStrategyToken(underlyingToken, minimumReservesRatio);
/// _togglePortStrategy
} else if (_params[0] == 0x06) {
(
address portStrategy,
address underlyingToken,
uint256 dailyManagementLimit,
uint256 reserveRatioManagementLimit
) = abi.decode(_params[1:], (address, address, uint256, uint256));
_togglePortStrategy(portStrategy, underlyingToken, dailyManagementLimit, reserveRatioManagementLimit);
/// _updatePortStrategy
} else if (_params[0] == 0x07) {
(
address portStrategy,
address underlyingToken,
uint256 dailyManagementLimit,
uint256 reserveRatioManagementLimit
) = abi.decode(_params[1:], (address, address, uint256, uint256));
_updatePortStrategy(portStrategy, underlyingToken, dailyManagementLimit, reserveRatioManagementLimit);
/// _setCoreBranchRouter
} else if (_params[0] == 0x08) {
(address coreBranchRouter, address coreBranchBridgeAgent) = abi.decode(_params[1:], (address, address));
IPort(localPortAddress).setCoreBranchRouter(coreBranchRouter, coreBranchBridgeAgent);
/// _sweep
} else if (_params[0] == 0x09) {
(address recipient) = abi.decode(_params[1:], (address));
IPort(localPortAddress).sweep(recipient);
/// Unrecognized Function Selector
} else {
revert UnrecognizedFunctionId();
}
}
/*///////////////////////////////////////////////////////////////
TOKEN MANAGEMENT INTERNAL FUNCTIONS
///////////////////////////////////////////////////////////////*/
/**
* @notice Function to deploy/add a token already active in the global environment in the Root Chain.
* Must be called from another chain.
* @param _globalAddress the address of the global virtualized token.
* @param _name token name.
* @param _symbol token symbol.
* @param _refundee the address of the excess gas receiver.
* @param _gParams Gas parameters for remote execution.
* @dev FUNC ID: 1
* @dev all hTokens have 18 decimals.
*/
function _receiveAddGlobalToken(
address _globalAddress,
string memory _name,
string memory _symbol,
uint8 _decimals,
address _refundee,
GasParams memory _gParams
) internal {
//Create Token
ERC20hToken newToken = ITokenFactory(hTokenFactoryAddress).createToken(_name, _symbol, _decimals);
// Encode Data
bytes memory params = abi.encode(_globalAddress, newToken);
// Pack FuncId
bytes memory payload = abi.encodePacked(bytes1(0x03), params);
//Send Cross-Chain request
IBridgeAgent(localBridgeAgentAddress).callOut{value: msg.value}(payable(_refundee), payload, _gParams);
}
/**
* @notice Function to deploy/add a token already active in the global environment in the Root Chain.
* Must be called from another chain.
* @param _newBranchRouter the address of the new branch router.
* @param _branchBridgeAgentFactory the address of the branch bridge agent factory.
* @param _rootBridgeAgent the address of the root bridge agent.
* @param _rootBridgeAgentFactory the address of the root bridge agent factory.
* @param _refundee the address of the excess gas receiver.
* @param _gParams Gas parameters for remote execution.
* @dev FUNC ID: 2
* @dev all hTokens have 18 decimals.
*/
function _receiveAddBridgeAgent(
address _newBranchRouter,
address _branchBridgeAgentFactory,
address _rootBridgeAgent,
address _rootBridgeAgentFactory,
address _refundee,
GasParams memory _gParams
) internal virtual {
// Save Port Address to memory
address _localPortAddress = localPortAddress;
// Check if msg.sender is a valid BridgeAgentFactory
if (!IPort(_localPortAddress).isBridgeAgentFactory(_branchBridgeAgentFactory)) {
revert UnrecognizedBridgeAgentFactory();
}
// Create BridgeAgent
address newBridgeAgent = IBridgeAgentFactory(_branchBridgeAgentFactory).createBridgeAgent(
_newBranchRouter, _rootBridgeAgent, _rootBridgeAgentFactory
);
// Check BridgeAgent Address
if (!IPort(_localPortAddress).isBridgeAgent(newBridgeAgent)) {
revert UnrecognizedBridgeAgent();
}
// Encode Data
bytes memory params = abi.encode(newBridgeAgent, _rootBridgeAgent);
// Pack FuncId
bytes memory payload = abi.encodePacked(bytes1(0x04), params);
//Send Cross-Chain request
IBridgeAgent(localBridgeAgentAddress).callOut{value: msg.value}(payable(_refundee), payload, _gParams);
}
/*///////////////////////////////////////////////////////////////
BRIDGE AGENT MANAGEMENT INTERNAL FUNCTIONS
///////////////////////////////////////////////////////////////*/
/**
* @notice Function to add/deactivate a Branch Bridge Agent Factory.
* @param _newBridgeAgentFactoryAddress the address of the new local bridge agent factory.
* @dev FUNC ID: 3
*/
function _toggleBranchBridgeAgentFactory(address _newBridgeAgentFactoryAddress) internal {
IPort(localPortAddress).toggleBridgeAgentFactory(_newBridgeAgentFactoryAddress);
}
/*///////////////////////////////////////////////////////////////
PORT STRATEGIES INTERNAL FUNCTIONS
///////////////////////////////////////////////////////////////*/
/**
* @notice Function to add/remove a token to be used by Port Strategies.
* @param _underlyingToken the address of the underlying token.
* @param _minimumReservesRatio the minimum reserves ratio the Port must have.
* @dev FUNC ID: 4
*/
function _toggleStrategyToken(address _underlyingToken, uint256 _minimumReservesRatio) internal {
IPort(localPortAddress).toggleStrategyToken(_underlyingToken, _minimumReservesRatio);
}
/**
* @notice Function to update the minimum reserves ratio of a token to be used by Port Strategies.
* @param _underlyingToken the address of the underlying token.
* @param _minimumReservesRatio the minimum reserves ratio the Port must have.
* @dev FUNC ID: 5
*/
function _updateStrategyToken(address _underlyingToken, uint256 _minimumReservesRatio) internal {
IPort(localPortAddress).updateStrategyToken(_underlyingToken, _minimumReservesRatio);
}
/**
* @notice Function to add or remove a strategy that manages balances of a strategy token from this branch's port.
* Must be called from another chain.
* @param _portStrategy the address of the port strategy.
* @param _underlyingToken the address of the underlying token.
* @param _dailyManagementLimit the daily management limit.
* @param _reserveRatioManagementLimit the reserve ratio management limit.
* @dev FUNC ID: 6
*/
function _togglePortStrategy(
address _portStrategy,
address _underlyingToken,
uint256 _dailyManagementLimit,
uint256 _reserveRatioManagementLimit
) internal {
IPort(localPortAddress).togglePortStrategy(
_portStrategy, _underlyingToken, _dailyManagementLimit, _reserveRatioManagementLimit
);
}
/**
* @notice Function to update a strategy that manages balances of a strategy token from this branch's port.
* Must be called from another chain.
* @param _portStrategy the address of the port strategy.
* @param _underlyingToken the address of the underlying token.
* @param _dailyManagementLimit the daily management limit.
* @param _reserveRatioManagementLimit the reserve ratio management limit.
* @dev FUNC ID: 7
*/
function _updatePortStrategy(
address _portStrategy,
address _underlyingToken,
uint256 _dailyManagementLimit,
uint256 _reserveRatioManagementLimit
) internal {
IPort(localPortAddress).updatePortStrategy(
_portStrategy, _underlyingToken, _dailyManagementLimit, _reserveRatioManagementLimit
);
}
}// 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;
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 {IBranchRouter} from "./interfaces/IBranchRouter.sol";
import {
IBranchBridgeAgent as IBridgeAgent,
GasParams,
Deposit,
DepositInput,
DepositParams,
DepositMultipleInput,
DepositMultipleParams,
SettlementParams,
SettlementMultipleParams
} from "./interfaces/IBranchBridgeAgent.sol";
/// @title Base Branch Router Contract
/// @author MaiaDAO
contract BaseBranchRouter is IBranchRouter, ReentrancyGuard, Ownable {
using SafeTransferLib for address;
/*///////////////////////////////////////////////////////////////
BASE BRANCH ROUTER STATE
///////////////////////////////////////////////////////////////*/
/// @inheritdoc IBranchRouter
address public localPortAddress;
/// @inheritdoc IBranchRouter
address public override localBridgeAgentAddress;
/// @inheritdoc IBranchRouter
address public override bridgeAgentExecutorAddress;
/*///////////////////////////////////////////////////////////////
CONSTRUCTOR
///////////////////////////////////////////////////////////////*/
/// @notice Constructor for Base Branch Router.
constructor() {
_initializeOwner(msg.sender);
}
/*///////////////////////////////////////////////////////////////
INITIALIZATION FUNCTIONS
///////////////////////////////////////////////////////////////*/
/**
* @notice Initializes the Base Branch Router.
* @param _localBridgeAgentAddress The address of the local Bridge Agent.
*/
function initialize(address _localBridgeAgentAddress) public virtual onlyOwner {
require(_localBridgeAgentAddress != address(0), "Bridge Agent address cannot be 0");
renounceOwnership();
localBridgeAgentAddress = _localBridgeAgentAddress;
localPortAddress = IBridgeAgent(_localBridgeAgentAddress).localPortAddress();
bridgeAgentExecutorAddress = IBridgeAgent(_localBridgeAgentAddress).bridgeAgentExecutorAddress();
}
/*///////////////////////////////////////////////////////////////
VIEW FUNCTIONS
///////////////////////////////////////////////////////////////*/
/// @inheritdoc IBranchRouter
function getDepositEntry(uint32 _depositNonce) external view override returns (Deposit memory) {
return IBridgeAgent(localBridgeAgentAddress).getDepositEntry(_depositNonce);
}
/*///////////////////////////////////////////////////////////////
EXTERNAL FUNCTIONS
///////////////////////////////////////////////////////////////*/
/// @inheritdoc IBranchRouter
function callOut(bytes calldata _params, GasParams calldata _gParams)
external
payable
virtual
override
nonReentrant
{
IBridgeAgent(localBridgeAgentAddress).callOut{value: msg.value}(payable(msg.sender), _params, _gParams);
}
/// @inheritdoc IBranchRouter
function callOutAndBridge(bytes calldata _params, DepositInput calldata _dParams, GasParams calldata _gParams)
external
payable
virtual
override
nonReentrant
{
//Transfer tokens to this contract.
_transferAndApproveToken(_dParams.hToken, _dParams.token, _dParams.amount, _dParams.deposit);
//Perform call to bridge agent.
IBridgeAgent(localBridgeAgentAddress).callOutAndBridge{value: msg.value}(
payable(msg.sender), _params, _dParams, _gParams
);
}
/// @inheritdoc IBranchRouter
function callOutAndBridgeMultiple(
bytes calldata _params,
DepositMultipleInput calldata _dParams,
GasParams calldata _gParams
) external payable virtual override nonReentrant {
//Transfer tokens to this contract.
_transferAndApproveMultipleTokens(_dParams);
//Perform call to bridge agent.
IBridgeAgent(localBridgeAgentAddress).callOutAndBridgeMultiple{value: msg.value}(
payable(msg.sender), _params, _dParams, _gParams
);
}
/// @inheritdoc IBranchRouter
function retryDeposit(uint32 _depositNonce, bytes calldata _params, GasParams calldata _gParams)
external
payable
virtual
override
{
// Perform call to bridge agent.
IBridgeAgent(localBridgeAgentAddress).retryDeposit{value: msg.value}(
msg.sender, _depositNonce, _params, _gParams
);
}
/*///////////////////////////////////////////////////////////////
BRIDGE AGENT EXECUTOR EXTERNAL FUNCTIONS
///////////////////////////////////////////////////////////////*/
/// @inheritdoc IBranchRouter
function executeNoSettlement(bytes calldata) external payable virtual override {
revert UnrecognizedFunctionId();
}
/// @inheritdoc IBranchRouter
function executeSettlement(bytes calldata, SettlementParams memory) external payable virtual override {
revert UnrecognizedFunctionId();
}
/// @inheritdoc IBranchRouter
function executeSettlementMultiple(bytes calldata, SettlementMultipleParams memory)
external
payable
virtual
override
{
revert UnrecognizedFunctionId();
}
/*///////////////////////////////////////////////////////////////
INTERNAL FUNCTIONS
///////////////////////////////////////////////////////////////*/
/**
* @notice Internal function to transfer token into a contract.
* @param _hToken The address of the hToken.
* @param _token The address of the token.
* @param _amount The amount of the hToken.
* @param _deposit The amount of the token.
*/
function _transferAndApproveToken(address _hToken, address _token, uint256 _amount, uint256 _deposit)
internal
virtual
{
// Check if the local branch tokens are being spent
if (_amount - _deposit > 0) {
unchecked {
_hToken.safeTransferFrom(msg.sender, address(this), _amount - _deposit);
}
}
// Check if the underlying tokens are being spent
if (_deposit > 0) {
_token.safeTransferFrom(msg.sender, address(this), _deposit);
_token.safeApprove(localPortAddress, _deposit);
}
}
/**
* @notice Internal function to transfer multiple tokens into a contract.
* @param _dParams The DepositMultipleInput struct
*/
function _transferAndApproveMultipleTokens(DepositMultipleInput calldata _dParams) internal {
uint256 len = _dParams.hTokens.length;
for (uint256 i = 0; i < len;) {
_transferAndApproveToken(_dParams.hTokens[i], _dParams.tokens[i], _dParams.amounts[i], _dParams.deposits[i]);
unchecked {
++i;
}
}
}
/*///////////////////////////////////////////////////////////////
MODIFIERS
///////////////////////////////////////////////////////////////*/
/// @notice Modifier that verifies msg sender is the Bridge Agent Executor.
modifier requiresAgentExecutor() {
if (msg.sender != bridgeAgentExecutorAddress) revert UnrecognizedBridgeAgentExecutor();
_;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/*///////////////////////////////////////////////////////////////
STRUCTS
//////////////////////////////////////////////////////////////*/
/// @notice Struct for storing the gas parameters for a cross-chain call.
/// @param gasLimit gas units allocated for a cross-chain call execution.
/// @param remoteBranchExecutionGas native token amount to request for destiantion branch usage.
struct GasParams {
uint256 gasLimit;
uint256 remoteBranchExecutionGas;
}
/// @notice Struct for storing information about a deposit in a Branch Bridge Agent's state.
/// @param status status of the deposit. Has 3 states - ready, done, retrieve.
/// @param isSigned indicates if the deposit has been signed allowing Virtual Account usage.
/// @param owner owner of the deposit.
/// @param hTokens array of local hTokens addresses.
/// @param tokens array of underlying token addresses.
/// @param amounts array of total deposited amounts.
/// @param deposits array of underlying token deposited amounts.
struct Deposit {
uint8 status;
uint88 isSigned;
address owner;
address[] hTokens;
address[] tokens;
uint256[] amounts;
uint256[] deposits;
}
/// @notice Struct for inputting deposit information into a Branch Bridge Agent.
/// @param hToken local hToken address.
/// @param token underlying token address.
/// @param amount total amount to deposit.
/// @param deposit underlying token amount to deposit.
struct DepositInput {
address hToken;
address token;
uint256 amount;
uint256 deposit;
}
/// @notice Struct for inputting multiple asset deposit information into a Branch Bridge Agent.
/// @param hTokens array of local hTokens addresses.
/// @param tokens array of underlying token addresses.
/// @param amounts array of total amounts to deposit.
/// @param deposits array of underlying token amounts to deposit.
struct DepositMultipleInput {
address[] hTokens;
address[] tokens;
uint256[] amounts;
uint256[] deposits;
}
/// @notice Struct for encoding deposit information in a cross-chain message.
/// @param depositNonce deposit nonce.
/// @param hToken local hToken address.
/// @param token underlying token address.
/// @param amount total amount to deposit.
/// @param deposit underlying token amount to deposit.
struct DepositParams {
uint32 depositNonce;
address hToken;
address token;
uint256 amount;
uint256 deposit;
}
/// @notice Struct for encoding multiple asset deposit information in a cross-chain message.
/// @param numberOfAssets number of assets to deposit.
/// @param depositNonce deposit nonce.
/// @param hTokens array of local hTokens addresses.
/// @param tokens array of underlying token addresses.
/// @param amounts array of total amounts to deposit.
/// @param deposits array of underlying token amounts to deposit.
struct DepositMultipleParams {
uint8 numberOfAssets;
uint32 depositNonce;
address[] hTokens;
address[] tokens;
uint256[] amounts;
uint256[] deposits;
}
/// @notice Struct for storing information about a settlement in a Root Bridge Agent's state.
/// @param dstChainId destination chain for interaction.
/// @param status status of the settlement.
/// @param owner owner of the settlement.
/// @param recipient recipient of the settlement.
/// @param hTokens array of global hTokens addresses.
/// @param tokens array of underlying token addresses.
/// @param amounts array of total settled amounts.
/// @param deposits array of underlying token settled amounts.
struct Settlement {
uint16 dstChainId;
uint80 status;
address owner;
address recipient;
address[] hTokens;
address[] tokens;
uint256[] amounts;
uint256[] deposits;
}
/// @notice Struct for inputting token settlement information into a Root Bridge Agent.
/// @param globalAddress global hToken address.
/// @param amount total amount to settle.
/// @param deposit underlying token amount to settle.
struct SettlementInput {
address globalAddress;
uint256 amount;
uint256 deposit;
}
/// @notice Struct for inputting multiple asset settlement information into a Root Bridge Agent.
/// @param globalAddresses array of global hTokens addresses.
/// @param amounts array of total amounts to settle.
/// @param deposits array of underlying token amounts to settle.
struct SettlementMultipleInput {
address[] globalAddresses;
uint256[] amounts;
uint256[] deposits;
}
/// @notice Struct for encoding settlement information in a cross-chain message.
/// @param settlementNonce settlement nonce.
/// @param recipient recipient of the settlement.
/// @param hToken destination local hToken address.
/// @param token destination underlying token address.
/// @param amount total amount to settle.
/// @param deposit underlying token amount to settle.
struct SettlementParams {
uint32 settlementNonce;
address recipient;
address hToken;
address token;
uint256 amount;
uint256 deposit;
}
/// @notice Struct for encoding multiple asset settlement information in a cross-chain message.
/// @param numberOfAssets number of assets to settle.
/// @param recipient recipient of the settlement.
/// @param settlementNonce settlement nonce.
/// @param hTokens array of destination local hTokens addresses.
/// @param tokens array of destination underlying token addresses.
/// @param amounts array of total amounts to settle.
/// @param deposits array of underlying token amounts to settle.
struct SettlementMultipleParams {
uint8 numberOfAssets;
address recipient;
uint32 settlementNonce;
address[] hTokens;
address[] tokens;
uint256[] amounts;
uint256[] deposits;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {
GasParams,
Deposit,
DepositInput,
DepositMultipleInput,
DepositParams,
DepositMultipleParams,
SettlementParams,
SettlementMultipleParams
} from "./BridgeAgentStructs.sol";
import {ILayerZeroReceiver} from "./ILayerZeroReceiver.sol";
/**
* @title Branch Bridge Agent Contract
* @author MaiaDAO
* @notice Contract for deployment in Branch Chains of Omnichain System, responsible for interfacing with
* Users and Routers acting as a middleman to access LayerZero cross-chain messaging and requesting/depositing
* assets in the Branch Chain's Ports.
* @dev Bridge Agents allow for the encapsulation of business logic as well as standardize cross-chain communication,
* allowing for the creation of custom Routers to perform actions in response to local / remote user requests.
* This contract is designed for deployment in the Branch Chains of the Ulysses Omnichain Liquidity System.
* The Branch Bridge Agent is responsible for sending/receiving requests to/from the LayerZero Messaging Layer
* for execution, as well as requests tokens clearances and tx execution to the `BranchBridgeAgentExecutor`.
* Remote execution is "sandboxed" within 2 different layers/nestings:
* - 1: Upon receiving a request from LayerZero Messaging Layer to avoid blocking future requests due to
* execution reversion, ensuring our app is Non-Blocking.
* (See https://github.com/LayerZero-Labs/solidity-examples/blob/8e62ebc886407aafc89dbd2a778e61b7c0a25ca0/contracts/lzApp/NonblockingLzApp.sol)
* - 2: The call to `BranchBridgeAgentExecutor` is in charge of requesting token deposits for each remote
* interaction as well as performing the Router calls, if any of the calls initiated by the Router lead
* to an invalid state change both the token deposit clearances as well as the external interactions
* will be reverted and caught by the `BranchBridgeAgent`.
*
* **BRANCH BRIDGE AGENT SETTLEMENT FLAGs** Func IDs for calling these functions through the messaging layer
*
* | ID | DESCRIPTION |
* | ---- | ------------------------------------------------------------------------------------------------- |
* | 0x01 | Call to Branch without Settlement. |
* | 0x02 | Call to Branch with Settlement. |
* | 0x03 | Call to Branch with Settlement of Multiple Tokens. |
* | 0x04 | Call to `retrieveSettlement()`. (trigger `_fallback` for a settlement that has not been executed) |
* | 0x05 | Call to `_fallback()`. (reopens a deposit for asset redemption) |
*
* Encoding Scheme for different Root Bridge Agent Deposit Flags:
*
* - ht = hToken
* - t = Token
* - A = Amount
* - D = Deposit
* - b = bytes
* - n = number of assets
*
* | Flag | Deposit Info | Token Info | DATA |
* | ------ | --------------------------- | ----------------------- | ---- |
* | 1 byte | 4-25 bytes | 104 or (128 * n) bytes | |
* | | | hT - t - A - D | ... |
* | 0x1 | 20b(recipient) + 4b(nonce) | --- | ... |
* | 0x2 | 20b(recipient) + 4b(nonce) | 20b + 20b + 32b + 32b | ... |
* | 0x3 | 1b(n) + 20b(recipient) + 4b | 32b + 32b + 32b + 32b | ... |
*
* **Generic Contract Interaction Flow:**
* BridgeAgent.lzReceive() -> BridgeAgentExecutor.execute() -> Router.execute()
*
*/
interface IBranchBridgeAgent is ILayerZeroReceiver {
/*///////////////////////////////////////////////////////////////
VIEW FUNCTIONS
///////////////////////////////////////////////////////////////*/
/**
* @notice External function to return the Branch Chain's Local Port Address.
* @return address of the Branch Chain's Local Port.
*/
function localPortAddress() external view returns (address);
/**
* @notice External function to return the Branch Bridge Agent Executor Address.
* @return address of the Branch Bridge Agent Executor.
*/
function bridgeAgentExecutorAddress() external view returns (address);
/**
* @notice External function that returns a given deposit entry.
* @param depositNonce Identifier for user deposit.
*/
function getDepositEntry(uint32 depositNonce) external view returns (Deposit memory);
/*///////////////////////////////////////////////////////////////
USER AND BRANCH ROUTER FUNCTIONS
///////////////////////////////////////////////////////////////*/
/**
* @notice Function to perform a call to the Root Omnichain Router without token deposit.
* @param gasRefundee Address to return excess gas deposited in `msg.value` to.
* @param params enconded parameters to execute on the root chain router.
* @param gasParams gas parameters for the cross-chain call.
* @dev DEPOSIT ID: 1 (Call without deposit)
*/
function callOut(address payable gasRefundee, bytes calldata params, GasParams calldata gasParams)
external
payable;
/**
* @notice Function to perform a call to the Root Omnichain Router while depositing a single asset.
* @param depositOwnerAndGasRefundee Deposit owner and address to return excess gas deposited in `msg.value` to.
* @param params enconded parameters to execute on the root chain router.
* @param depositParams additional token deposit parameters.
* @param gasParams gas parameters for the cross-chain call.
* @dev DEPOSIT ID: 2 (Call with single deposit)
*/
function callOutAndBridge(
address payable depositOwnerAndGasRefundee,
bytes calldata params,
DepositInput memory depositParams,
GasParams calldata gasParams
) external payable;
/**
* @notice Function to perform a call to the Root Omnichain Router while depositing two or more assets.
* @param depositOwnerAndGasRefundee Deposit owner and address to return excess gas deposited in `msg.value` to.
* @param params enconded parameters to execute on the root chain router.
* @param depositParams additional token deposit parameters.
* @param gasParams gas parameters for the cross-chain call.
* @dev DEPOSIT ID: 3 (Call with multiple deposit)
*/
function callOutAndBridgeMultiple(
address payable depositOwnerAndGasRefundee,
bytes calldata params,
DepositMultipleInput memory depositParams,
GasParams calldata gasParams
) external payable;
/**
* @notice Perform a call to the Root Omnichain Router without token deposit with msg.sender information.
* @dev msg.sender is gasRefundee in signed calls.
* @param params enconded parameters to execute on the root chain router.
* @param gasParams gas parameters for the cross-chain call.
* @dev DEPOSIT ID: 4 (Call without deposit and verified sender)
*/
function callOutSigned(bytes calldata params, GasParams calldata gasParams) external payable;
/**
* @notice Function to perform a call to the Root Omnichain Router while depositing a single asset msg.sender.
* @dev msg.sender is depositOwnerAndGasRefundee in signed calls.
* @param params enconded parameters to execute on the root chain router.
* @param depositParams additional token deposit parameters.
* @param gasParams gas parameters for the cross-chain call.
* @param hasFallbackToggled flag to indicate if the fallback function was toggled.
* @dev DEPOSIT ID: 5 (Call with single deposit and verified sender)
*/
function callOutSignedAndBridge(
bytes calldata params,
DepositInput memory depositParams,
GasParams calldata gasParams,
bool hasFallbackToggled
) external payable;
/**
* @notice Function to perform a call to the Root Omnichain Router while
* depositing two or more assets with msg.sender.
* @dev msg.sender is depositOwnerAndGasRefundee in signed calls.
* @param params enconded parameters to execute on the root chain router.
* @param depositParams additional token deposit parameters.
* @param gasParams gas parameters for the cross-chain call.
* @param hasFallbackToggled flag to indicate if the fallback function was toggled.
* @dev DEPOSIT ID: 6 (Call with multiple deposit and verified sender)
*/
function callOutSignedAndBridgeMultiple(
bytes calldata params,
DepositMultipleInput memory depositParams,
GasParams calldata gasParams,
bool hasFallbackToggled
) external payable;
/*///////////////////////////////////////////////////////////////
DEPOSIT EXTERNAL FUNCTIONS
///////////////////////////////////////////////////////////////*/
/**
* @notice Function to perform a call to the Root Omnichain Environment
* retrying a failed non-signed deposit that hasn't been executed yet.
* @param owner address of the deposit owner.
* @param depositNonce Identifier for user deposit.
* @param params parameters to execute on the root chain router.
* @param gasParams gas parameters for the cross-chain call.
*/
function retryDeposit(address owner, uint32 depositNonce, bytes calldata params, GasParams calldata gasParams)
external
payable;
/**
* @notice Function to perform a call to the Root Omnichain Environment
* retrying a failed signed deposit that hasn't been executed yet.
* @param depositNonce Identifier for user deposit.
* @param params parameters to execute on the root chain router.
* @param gasParams gas parameters for the cross-chain call.
* @param hasFallbackToggled flag to indicate if the fallback function was toggled.
*/
function retryDepositSigned(
uint32 depositNonce,
bytes calldata params,
GasParams calldata gasParams,
bool hasFallbackToggled
) external payable;
/**
* @notice External function to request tokens back to branch chain after failing omnichain environment interaction.
* @param depositNonce Identifier for user deposit to retrieve.
* @param gasParams gas parameters for the cross-chain call.
* @dev DEPOSIT ID: 8
*/
function retrieveDeposit(uint32 depositNonce, GasParams calldata gasParams) external payable;
/**
* @notice External function to retry a failed Deposit entry on this branch chain.
* @param depositNonce Identifier for user deposit.
* @param recipient address to receive the redeemed tokens.
*/
function redeemDeposit(uint32 depositNonce, address recipient) external;
/**
* @notice External function to retry a failed Deposit entry on this branch chain.
* @param depositNonce Identifier for user deposit.
* @param recipient address to receive the redeemed tokens.
* @param localTokenAddress address of the local token to redeem.
*/
function redeemDeposit(uint32 depositNonce, address recipient, address localTokenAddress) external;
/*///////////////////////////////////////////////////////////////
SETTLEMENT EXTERNAL FUNCTIONS
///////////////////////////////////////////////////////////////*/
/**
* @notice External function to retry a failed Settlement entry on the root chain.
* @param settlementNonce Identifier for user settlement.
* @param params parameters to execute on the root chain router.
* @param gasParams gas parameters for the cross-chain call to root chain and for the settlement to branch.
* @param hasFallbackToggled flag to indicate if the fallback function should be toggled.
* @dev DEPOSIT ID: 7
*/
function retrySettlement(
uint32 settlementNonce,
bytes calldata params,
GasParams[2] calldata gasParams,
bool hasFallbackToggled
) external payable;
/*///////////////////////////////////////////////////////////////
TOKEN MANAGEMENT EXTERNAL FUNCTIONS
///////////////////////////////////////////////////////////////*/
/**
* @notice Function to request balance clearance from a Port to a given user.
* @param recipient token receiver.
* @param hToken local hToken addresse to clear balance for.
* @param token native / underlying token addresse to clear balance for.
* @param amount amounts of token to clear balance for.
* @param deposit amount of native / underlying tokens to clear balance for.
*/
function bridgeIn(address recipient, address hToken, address token, uint256 amount, uint256 deposit) external;
/**
* @notice Function to request balance clearance from a Port to a given address.
* @param recipient token receiver.
* @param sParams encode packed multiple settlement info.
*/
function bridgeInMultiple(address recipient, SettlementMultipleParams calldata sParams) external;
/*///////////////////////////////////////////////////////////////
EVENTS
///////////////////////////////////////////////////////////////*/
/// @notice Event emitted when a deposit is successfully and fully redeemed.
/// @param depositNonce Identifier for user deposit.
/// @param recipient address to receive the redeemed tokens.
event RedeemDeposit(uint256 indexed depositNonce, address indexed recipient);
/// @notice Event emitted when a single token is redeemed from a deposit and there are other tokens left to redeem.
/// @param depositNonce Identifier for user deposit.
/// @param recipient address to receive the redeemed tokens.
/// @param localTokenAddress address of the local token to redeem.
event RedeemDeposit(uint256 indexed depositNonce, address indexed recipient, address indexed localTokenAddress);
/// @notice Event emitted when a settlement nonce is executed successfully.
/// @param nonce Identifier for user settlement.
event LogExecute(uint256 indexed nonce);
/// @notice Event emitted when fallback is received for a failed deposit nonce.
/// @param nonce Identifier for user deposit.
event LogFallback(uint256 indexed nonce);
/// @notice Event emitted after a message is sent to the Layer Zero Endpoint.
/// @param gasLimit gas limit for the cross-chain call.
/// @param remoteBranchExecutionGas native gas tokens to be sent to the remote branch.
event LogGasParams(uint256 indexed gasLimit, uint256 indexed remoteBranchExecutionGas);
/*///////////////////////////////////////////////////////////////
ERRORS
///////////////////////////////////////////////////////////////*/
/// @notice Error emitted when the provided Root Bridge Agent Address is invalid.
error InvalidRootBridgeAgentAddress();
/// @notice Error emitted when the provided Branch Port Address is invalid.
error InvalidBranchPortAddress();
/// @notice Error emitted when the provided Layer Zero Endpoint Address is invalid.
error InvalidEndpointAddress();
/// @notice Error emitted when the Branch Bridge Agent does not recognize the action flag.
error UnknownFlag();
/// @notice Error emitted when a settlement nonce fails to execute and does not have fallback enabled.
error ExecutionFailure();
/// @notice Error emitted when a Layer Zero remote caller in not recognized as the Root Bridge Agent.
error LayerZeroUnauthorizedCaller();
/// @notice Error emitted when the caller is not the local Layer Zero Endpoint contract.
error LayerZeroUnauthorizedEndpoint();
/// @notice Error emitted when the settlement nonce has already been executed.
error AlreadyExecutedTransaction();
/// @notice Error emitted when the local hToken address is zero.
error InvalidLocalAddress();
/// @notice Error emitted when the deposit information is not valid.
error InvalidInput();
/// @notice Error emitted when the the wrong retryDeposit function for a deposit type (Signed or Unsigned).
error WrongDepositType();
/// @notice Error emitted when caller is not the deposit owner.
error NotDepositOwner();
/// @notice Error emitted when the action of deposit nonce is not retryabable.
error DepositRetryUnavailableUseCallout();
/// @notice Error emitted when the deposit nonce is not in a redeemable state.
error DepositRedeemUnavailable();
/// @notice Error emitted when the deposit nonce is not in a retryable state.
error DepositAlreadyRetrieved();
/// @notice Error emitted when the caller is not the Branch Bridge Agent's Router
error UnrecognizedRouter();
/// @notice Error emitted when the caller is not the Branch Bridge Agent's Executors
error UnrecognizedBridgeAgentExecutor();
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @title Branch Bridge Agent Factory Contract
* @author MaiaDAO
* @notice Factory contract for allowing permissionless deployment of new Branch Bridge Agents. May or may not have a
* corresponding Branch Router contract allowing for customized execution before and / or after cross-chain
* message execution / emission.
*/
interface IBranchBridgeAgentFactory {
/*///////////////////////////////////////////////////////////////
BRIDGE AGENT FUNCTIONS
///////////////////////////////////////////////////////////////*/
/**
* @notice Creates a new Branch Bridge Agent.
* @param newRootRouterAddress New Root Router Address.
* @param rootBridgeAgentAddress Root Bridge Agent Address.
* @param rootBridgeAgentFactoryAddress Root Bridge Agent Factory Address.
* @return newBridgeAgent New Bridge Agent Address.
*/
function createBridgeAgent(
address newRootRouterAddress,
address rootBridgeAgentAddress,
address rootBridgeAgentFactoryAddress
) external returns (address newBridgeAgent);
/*///////////////////////////////////////////////////////////////
EVENTS
///////////////////////////////////////////////////////////////*/
/// @notice Emitted when a new Bridge Agent is created.
event BridgeAgentAdded(address indexed _bridgeAgent);
/*///////////////////////////////////////////////////////////////
ERRORS
///////////////////////////////////////////////////////////////*/
/// @notice Error emitted when caller is not the Core Branch Router.
error UnrecognizedCoreBranchRouter();
/// @notice Error emitted when Core Root Bridge Agent is received as zero address.
error InvalidInputCannotBeZeroAddress();
/// @notice Error emitted when the Root Bridge Agent Factory does not match with the Branch Bridge Agent Factory.
error InvalidInputFactoryMismatch();
}// 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;
import {
GasParams,
Deposit,
DepositInput,
DepositMultipleInput,
SettlementParams,
SettlementMultipleParams
} from "./IBranchBridgeAgent.sol";
/**
* @title BaseBranchRouter Contract
* @author MaiaDAO
* @notice Base Branch Contract for interfacing with Branch Bridge Agents.
* This contract for deployment in Branch Chains of the Ulysses Omnichain System,
* additional logic can be implemented to perform actions before sending cross-chain
* requests, as well as in response to requests from the Root Omnichain Environment.
*/
interface IBranchRouter {
/*///////////////////////////////////////////////////////////////
VIEW / STATE
///////////////////////////////////////////////////////////////*/
/// @notice External function to return the Branch Chain's Local Port Address.
function localPortAddress() external view returns (address);
/// @notice Address for local Branch Bridge Agent who processes requests and interacts with local port.
function localBridgeAgentAddress() external view returns (address);
/// @notice Local Bridge Agent Executor Address.
function bridgeAgentExecutorAddress() external view returns (address);
/// @notice External function that returns a given deposit entry.
/// @param depositNonce Identifier for user deposit.
function getDepositEntry(uint32 depositNonce) external view returns (Deposit memory);
/*///////////////////////////////////////////////////////////////
EXTERNAL FUNCTIONS
///////////////////////////////////////////////////////////////*/
/**
* @notice Function to perform a call to the Root Omnichain Router without token deposit.
* @param params enconded parameters to execute on the root chain.
* @param gParams gas parameters for the cross-chain call.
* @dev ACTION ID: 1 (Call without deposit)
*
*/
function callOut(bytes calldata params, GasParams calldata gParams) external payable;
/**
* @notice Function to perform a call to the Root Omnichain Router while depositing a single asset.
* @param params encoded parameters to execute on the root chain.
* @param dParams additional token deposit parameters.
* @param gParams gas parameters for the cross-chain call.
* @dev ACTION ID: 2 (Call with single deposit)
*
*/
function callOutAndBridge(bytes calldata params, DepositInput calldata dParams, GasParams calldata gParams)
external
payable;
/**
* @notice Function to perform a call to the Root Omnichain Router while depositing two or more assets.
* @param params encoded parameters to execute on the root chain.
* @param dParams additional token deposit parameters.
* @param gParams gas parameters for the cross-chain call.
* @dev ACTION ID: 3 (Call with multiple deposit)
*
*/
function callOutAndBridgeMultiple(
bytes calldata params,
DepositMultipleInput calldata dParams,
GasParams calldata gParams
) external payable;
/**
* @notice Function to retry a deposit that has failed.
* @param _depositNonce Identifier for user deposit.
* @param _params encoded router parameters to execute on the root chain.
* @param _gParams gas parameters for the cross-chain call.
*/
function retryDeposit(uint32 _depositNonce, bytes calldata _params, GasParams calldata _gParams) external payable;
/*///////////////////////////////////////////////////////////////
LAYERZERO EXTERNAL FUNCTIONS
///////////////////////////////////////////////////////////////*/
/**
* @notice Function responsible of executing a branch router response.
* @param params data received from messaging layer.
*/
function executeNoSettlement(bytes calldata params) external payable;
/**
* @dev Function responsible of executing a crosschain request without any deposit.
* @param params data received from messaging layer.
* @param sParams SettlementParams struct.
*/
function executeSettlement(bytes calldata params, SettlementParams calldata sParams) external payable;
/**
* @dev Function responsible of executing a crosschain request which contains
* cross-chain deposit information attached.
* @param params data received from messaging layer.
* @param sParams SettlementParams struct containing deposit information.
*
*/
function executeSettlementMultiple(bytes calldata params, SettlementMultipleParams calldata sParams)
external
payable;
/*///////////////////////////////////////////////////////////////
ERRORS
///////////////////////////////////////////////////////////////*/
/// @notice Error emitted when the Branch Router does not recognize the function ID.
error UnrecognizedFunctionId();
/// @notice Error emitted when caller is not the Branch Bridge Agent Executor.
error UnrecognizedBridgeAgentExecutor();
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {GasParams} from "./IBranchBridgeAgent.sol";
/**
* @title Core Branch Router Contract
* @author MaiaDAO
* @notice Core Branch Router implementation for deployment in Branch Chains.
* This contract is allows users to permissionlessly add new tokens
* or Bridge Agents to the system. As well as executes key governance
* enabled system functions (i.e. `addBridgeAgentFactory`).
* @dev Func IDs for calling these functions through messaging layer:
*
* **CROSS-CHAIN MESSAGING FUNCIDs**
* Func IDs for calling these functions through messaging layer
*
* | FUNC ID | FUNC NAME |
* | ------- | ------------------------------ |
* | 0x01 | addGlobalToken |
* | 0x02 | addBridgeAgent |
* | 0x03 | toggleBranchBridgeAgentFactory |
* | 0x04 | toggleStrategyToken |
* | 0x05 | updateStrategyToken |
* | 0x06 | togglePortStrategy |
* | 0x07 | updatePortStrategy |
* | 0x08 | setCoreBranchRouter |
* | 0x09 | sweep |
*
*/
interface ICoreBranchRouter {
/*///////////////////////////////////////////////////////////////
EXTERNAL FUNCTIONS
///////////////////////////////////////////////////////////////*/
/**
* @notice Function to deploy / add a token already present in the global environment to a branch chain.
* @param _globalAddress the address of the global virtualized token.
* @param _dstChainId the chain to which the token will be added.
* @param _gasParams Gas parameters for each remote execution step.
*/
function addGlobalToken(address _globalAddress, uint256 _dstChainId, GasParams[3] calldata _gasParams)
external
payable;
/**
* @notice Function to add a token that's not available in the global environment from this branch chain.
* @param _underlyingAddress the address of the token to be added.
* @param _gasParams Gas parameters for remote execution.
*/
function addLocalToken(address _underlyingAddress, GasParams calldata _gasParams) external payable;
/*///////////////////////////////////////////////////////////////
ERRORS
///////////////////////////////////////////////////////////////*/
/// @notice Error emitted when caller is not the connected Branch Bridge Agent.
error UnrecognizedBridgeAgent();
/// @notice Error emitted when caller is not the Branch Bridge Agent Factory.
error UnrecognizedBridgeAgentFactory();
}// 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;
import {ERC20hToken} from "../token/ERC20hToken.sol";
/**
* @title ERC20hTokenBranchFactory Contract
* @author MaiaDAO
* @notice Factory contract allowing for permissionless deployment of new Branch hTokens in Branch
* Chains of Ulysses Omnichain Liquidity Protocol.
* @dev This contract is called by the chain's Core Branch Router.
*/
interface IERC20hTokenBranchFactory {
/*///////////////////////////////////////////////////////////////
hTOKEN FUNCTIONS
///////////////////////////////////////////////////////////////*/
/**
* @notice Function to create a new Branch hToken.
* @param _name Name of the Token.
* @param _symbol Symbol of the Token.
* @param _decimals Decimals of the Token.
*/
function createToken(string memory _name, string memory _symbol, uint8 _decimals)
external
returns (ERC20hToken newToken);
/*///////////////////////////////////////////////////////////////
ERRORS
///////////////////////////////////////////////////////////////*/
/// @notice Error emitted when caller is not the Core Branch Router.
error UnrecognizedCoreRouter();
/// @notice Error emitted when the Port Address is the zero address.
error UnrecognizedPort();
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.5.0;
interface ILayerZeroReceiver {
/*///////////////////////////////////////////////////////////////
LAYER ZERO FUNCTIONS
///////////////////////////////////////////////////////////////*/
/**
* @notice LayerZero endpoint will invoke this function to deliver the message on the destination
* @param _srcChainId the source endpoint identifier
* @param _srcAddress the source sending contract address from the source chain
* @param _nonce the ordered message nonce
* @param _payload the signed payload is the UA bytes has encoded to be sent
*/
function lzReceive(uint16 _srcChainId, bytes calldata _srcAddress, uint64 _nonce, bytes calldata _payload)
external
payable
returns (bool);
/**
* @notice External function to receive cross-chain messages from LayerZero Endpoint Contract without blocking.
* @param _endpoint address of the LayerZero Endpoint Contract.
* @param _srcAddress address path of the recipient + sender.
* @param _payload Calldata for function call.
*/
function lzReceiveNonBlocking(
address _endpoint,
uint16 _srcChainId,
bytes calldata _srcAddress,
bytes calldata _payload
) external payable;
/**
* @notice Only when the BridgeAgent needs to resume the message flow in blocking mode and clear the stored payload.
* @param _srcChainId the chainId of the source chain
* @param _srcAddress the contract address of the source contract at the source chain
*/
function forceResumeReceive(uint16 _srcChainId, bytes calldata _srcAddress) external;
}// 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":"_hTokenFactoryAddress","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AlreadyInitialized","type":"error"},{"inputs":[],"name":"NewOwnerIsZeroAddress","type":"error"},{"inputs":[],"name":"NoHandoverRequest","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"inputs":[],"name":"UnrecognizedBridgeAgent","type":"error"},{"inputs":[],"name":"UnrecognizedBridgeAgentExecutor","type":"error"},{"inputs":[],"name":"UnrecognizedBridgeAgentFactory","type":"error"},{"inputs":[],"name":"UnrecognizedFunctionId","type":"error"},{"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"},{"inputs":[{"internalType":"address","name":"_globalAddress","type":"address"},{"internalType":"uint256","name":"_dstChainId","type":"uint256"},{"components":[{"internalType":"uint256","name":"gasLimit","type":"uint256"},{"internalType":"uint256","name":"remoteBranchExecutionGas","type":"uint256"}],"internalType":"struct GasParams[3]","name":"_gParams","type":"tuple[3]"}],"name":"addGlobalToken","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"_underlyingAddress","type":"address"},{"components":[{"internalType":"uint256","name":"gasLimit","type":"uint256"},{"internalType":"uint256","name":"remoteBranchExecutionGas","type":"uint256"}],"internalType":"struct GasParams","name":"_gParams","type":"tuple"}],"name":"addLocalToken","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"bridgeAgentExecutorAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"_params","type":"bytes"},{"components":[{"internalType":"uint256","name":"gasLimit","type":"uint256"},{"internalType":"uint256","name":"remoteBranchExecutionGas","type":"uint256"}],"internalType":"struct GasParams","name":"_gParams","type":"tuple"}],"name":"callOut","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes","name":"_params","type":"bytes"},{"components":[{"internalType":"address","name":"hToken","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"deposit","type":"uint256"}],"internalType":"struct DepositInput","name":"_dParams","type":"tuple"},{"components":[{"internalType":"uint256","name":"gasLimit","type":"uint256"},{"internalType":"uint256","name":"remoteBranchExecutionGas","type":"uint256"}],"internalType":"struct GasParams","name":"_gParams","type":"tuple"}],"name":"callOutAndBridge","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes","name":"_params","type":"bytes"},{"components":[{"internalType":"address[]","name":"hTokens","type":"address[]"},{"internalType":"address[]","name":"tokens","type":"address[]"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"},{"internalType":"uint256[]","name":"deposits","type":"uint256[]"}],"internalType":"struct DepositMultipleInput","name":"_dParams","type":"tuple"},{"components":[{"internalType":"uint256","name":"gasLimit","type":"uint256"},{"internalType":"uint256","name":"remoteBranchExecutionGas","type":"uint256"}],"internalType":"struct GasParams","name":"_gParams","type":"tuple"}],"name":"callOutAndBridgeMultiple","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"cancelOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"completeOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes","name":"_params","type":"bytes"}],"name":"executeNoSettlement","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes","name":"","type":"bytes"},{"components":[{"internalType":"uint32","name":"settlementNonce","type":"uint32"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"address","name":"hToken","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"deposit","type":"uint256"}],"internalType":"struct SettlementParams","name":"","type":"tuple"}],"name":"executeSettlement","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes","name":"","type":"bytes"},{"components":[{"internalType":"uint8","name":"numberOfAssets","type":"uint8"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint32","name":"settlementNonce","type":"uint32"},{"internalType":"address[]","name":"hTokens","type":"address[]"},{"internalType":"address[]","name":"tokens","type":"address[]"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"},{"internalType":"uint256[]","name":"deposits","type":"uint256[]"}],"internalType":"struct SettlementMultipleParams","name":"","type":"tuple"}],"name":"executeSettlementMultiple","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint32","name":"_depositNonce","type":"uint32"}],"name":"getDepositEntry","outputs":[{"components":[{"internalType":"uint8","name":"status","type":"uint8"},{"internalType":"uint88","name":"isSigned","type":"uint88"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"address[]","name":"hTokens","type":"address[]"},{"internalType":"address[]","name":"tokens","type":"address[]"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"},{"internalType":"uint256[]","name":"deposits","type":"uint256[]"}],"internalType":"struct Deposit","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"hTokenFactoryAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_localBridgeAgentAddress","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"localBridgeAgentAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"localPortAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"result","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"ownershipHandoverExpiresAt","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"requestOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint32","name":"_depositNonce","type":"uint32"},{"internalType":"bytes","name":"_params","type":"bytes"},{"components":[{"internalType":"uint256","name":"gasLimit","type":"uint256"},{"internalType":"uint256","name":"remoteBranchExecutionGas","type":"uint256"}],"internalType":"struct GasParams","name":"_gParams","type":"tuple"}],"name":"retryDeposit","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"payable","type":"function"}]Contract Creation Code
60a0346100ac57601f612a6338819003918201601f19168301916001600160401b038311848410176100b1578084926020946040528339810103126100ac57516001600160a01b03811681036100ac57600160005533638b78c6d819553360007f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08180a360805260405161299b90816100c88239608051818181610fc501528181611ab50152611c480152f35b600080fd5b634e487b7160e01b600052604160045260246000fdfe608060408181526004918236101561001657600080fd5b60009260e08435811c9283630d9389ec146120ee5750826315859950146120295782631e2bcb1a14611f745782632569296214611f0b578483632786d29314611ad957508263448c8a2014611a6957826346fcff1814610e2957826351e372b014610cde578263546af93114610c8a57826354d1f13d14610c265782635abfaf6c14610bd2578263715018a614610b5257826383dd527d146108a557826384cb82b6146107fe5782638da5cb5b1461078b578263c4d66de81461052e578263cd9f6031146104da578263f04e283e14610412578263f2fde38b1461035d578263f4f79eb5146102c0578263fa536bec1461016f57505063fee81cf41461011b57600080fd5b3461016b5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261016b57602091610155612341565b9063389a75e1600c525281600c20549051908152f35b5080fd5b8484917ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102bd5767ffffffffffffffff90833582811161016b576101ba9036908601612385565b505060c07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc3601126102bd575081519060c082019081118282101761028f57825260243563ffffffff8116810361028a57815273ffffffffffffffffffffffffffffffffffffffff604435818116810361028a576020830152606435818116810361028a5783830152608435908116810361028a57606082015260a435608082015260a060c435910152517f48d3a3f7000000000000000000000000000000000000000000000000000000008152fd5b600080fd5b6041847f4e487b71000000000000000000000000000000000000000000000000000000006000525260246000fd5b80fd5b50828460607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102bd57823567ffffffffffffffff811161016b5761030b9036908501612385565b5050817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc3601126102bd5750517f48d3a3f7000000000000000000000000000000000000000000000000000000008152fd5b848260207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261016b57610391612341565b9061039a6125ea565b8160601b15610407575073ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffffffffffffffffffffffffffffffffffff748739278181547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a35580f35b637448fbae8352601cfd5b508360207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102bd57610446612341565b61044e6125ea565b63389a75e1600c528082526020600c2092835442116104cf57508173ffffffffffffffffffffffffffffffffffffffff929355167fffffffffffffffffffffffffffffffffffffffffffffffffffffffff748739278181547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a35580f35b636f5e88188352601cfd5b5050503461016b57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261016b5760209073ffffffffffffffffffffffffffffffffffffffff600254169051908152f35b5090503461078757602091827ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126106d35761056a612341565b916105736125ea565b73ffffffffffffffffffffffffffffffffffffffff80931693841561072e5761059a6125ea565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffff748739278181547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3557fffffffffffffffffffffffff0000000000000000000000000000000000000000948086600254161760025583517f5abfaf6c00000000000000000000000000000000000000000000000000000000815282818581855afa9081156107245783929187918a916106e7575b50168760015416176001558451938480927f546af9310000000000000000000000000000000000000000000000000000000082525afa9283156106de575085926106a4575b50501690600354161760035580f35b90809250813d83116106d7575b6106bb818361242e565b810103126106d3576106cc90612622565b3880610695565b8380fd5b503d6106b1565b513d87823e3d90fd5b938092508491503d831161071d575b610700818361242e565b8101031261071957856107138493612622565b38610650565b8780fd5b503d6106f6565b85513d8a823e3d90fd5b8060649351927f08c379a000000000000000000000000000000000000000000000000000000000845283015260248201527f427269646765204167656e7420616464726573732063616e6e6f7420626520306044820152fd5b8280fd5b5050503461016b57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261016b5760209073ffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffff7487392754915191168152f35b50828460807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102bd5761083361247d565b5060243567ffffffffffffffff811161016b576108539036908501612385565b5050817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbc3601126102bd5750517f48d3a3f7000000000000000000000000000000000000000000000000000000008152fd5b915092346102bd57602092837ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261016b579060c0916108e561247d565b9181516108f181612412565b81815281878201528183820152606094818680809401528260808201528260a082015201528063ffffffff602473ffffffffffffffffffffffffffffffffffffffff95866002541686519b8c9485937f83dd527d00000000000000000000000000000000000000000000000000000000855216908301525afa968715610b46578197610a2d575b6affffffffffffffffffffff87610a298a896109c58a8a8a805198888a998a5260ff885116818b01528701511681890152850151168187015283015182608087015261010086019061256c565b9060c0610a18610a056080860151947fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe095868a83030160a08b015261256c565b60a08601518589830301848a01526125b6565b9301519185840301908501526125b6565b0390f35b909196503d8083833e610a40818361242e565b810190868183031261078757805167ffffffffffffffff91828211610b42570186818403126106d357885193610a7585612412565b610a7e82612643565b8552888201516affffffffffffffffffffff8116810361016b5789860152610aa78a8301612622565b8a8601528682015183811161016b5784610ac2918401612651565b87860152608082015183811161016b5784610ade918401612651565b608086015260a082015183811161016b5784610afb9184016126b7565b60a086015260c08201519283116102bd575092610b306affffffffffffffffffffff999693610a299896936109c596016126b7565b60c08201529750929481949250610978565b8480fd5b509051903d90823e3d90fd5b84807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102bd57610b846125ea565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffff748739278181547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a35580f35b5050503461016b57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261016b5760209073ffffffffffffffffffffffffffffffffffffffff600154169051908152f35b84807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102bd5763389a75e1600c52338152806020600c2055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c928280a280f35b5050503461016b57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261016b5760209073ffffffffffffffffffffffffffffffffffffffff600354169051908152f35b8484917ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc9083823601126107875767ffffffffffffffff908535828111610b4257610d2c9036908801612385565b505060243592828411610b4257833603011261078757835192610d4e84612412565b610d5983870161246f565b8452610d6760248401612364565b6020850152604483013563ffffffff8116810361028a5785850152606483013582811161016b57610d9d908736918601016124a8565b6060850152608483013582811161016b57610dbd908736918601016124a8565b608085015260a483013582811161016b57610ddd9087369186010161250e565b60a085015260c48301359182116102bd575060c09185610e00923692010161250e565b910152517f48d3a3f7000000000000000000000000000000000000000000000000000000008152fd5b848285602093847ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126106d35767ffffffffffffffff92803584811161160157610e789036908301612385565b73ffffffffffffffffffffffffffffffffffffffff9081600354163303611a41578015611a155782357fff00000000000000000000000000000000000000000000000000000000000000167f0100000000000000000000000000000000000000000000000000000000000000810361112d575080600111610719578201937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff838603011261112957610f2c60018301612364565b602183013587811161112557856001610f47928601016128eb565b94604184013588811161112157610f648260018d938801016128eb565b610f89610f736061880161246f565b9360a1610f8260818a01612364565b9801612715565b97610fbf8a5194859384937fe8a0aed30000000000000000000000000000000000000000000000000000000085528b850161286b565b03818c877f0000000000000000000000000000000000000000000000000000000000000000165af1908115611117579083918a916110ea575b508188519b8c019316835216868a015285895260608901968988109088111761028f576021889961106d928989527f0300000000000000000000000000000000000000000000000000000000000000608083015261105d82518092608185019061273d565b810103600181018952018761242e565b806002541691823b15610719576110b488958751988996879586957f114357cb00000000000000000000000000000000000000000000000000000000875216908501612932565b039134905af19081156110e157506110cd575b50505b80f35b6110d6906123fe565b6102bd5780826110c7565b513d84823e3d90fd5b61110a91508b3d8d11611110575b611102818361242e565b81019061283f565b8b610ff8565b503d6110f8565b87513d8b823e3d90fd5b8980fd5b8880fd5b8680fd5b909193959798929496507f0200000000000000000000000000000000000000000000000000000000000000811460001461146f575080600111610719578301947fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8487030112611129576111a360018401612364565b946111b060218501612364565b956111bd60418601612364565b9084806111e56111cf60618a01612364565b9560a16111de60818c01612364565b9a01612715565b99169216918560015416938a517fd3fbcc2e000000000000000000000000000000000000000000000000000000008152828a8201528681602481895afa908115611465578d91611448575b5015611420578a517fae178a8f000000000000000000000000000000000000000000000000000000008152928716838a015260248301849052861660448301528490829060649082908e905af19283156113db5784918b946113e5575b50906024918a51928380927ffb6f948d0000000000000000000000000000000000000000000000000000000082528988168c8301525afa9081156113db578a916113ae575b50156113865760217f04000000000000000000000000000000000000000000000000000000000000009795938997959361105d61134661137261106d968f9e51928391878301968790602090939293604083019473ffffffffffffffffffffffffffffffffffffffff809216845216910152565b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0810183528261242e565b8a519b8c948501525180928585019061273d565b8588517f541ba3f8000000000000000000000000000000000000000000000000000000008152fd5b6113ce9150843d86116113d4575b6113c6818361242e565b810190612976565b8a6112d2565b503d6113bc565b89513d8c823e3d90fd5b8281939295503d8311611419575b6113fd818361242e565b810103126111215783611411602492612622565b93909161128d565b503d6113f3565b888b517fdbf8f2a1000000000000000000000000000000000000000000000000000000008152fd5b61145f9150873d89116113d4576113c6818361242e565b8d611230565b8c513d8f823e3d90fd5b90979694939295507f03000000000000000000000000000000000000000000000000000000000000008114600014611538575086869760011161112957827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff918101030112610b42576114e56001859201612364565b16926001541692833b15610b42576024859283855196879485937f56a43fde0000000000000000000000000000000000000000000000000000000085528401525af19081156110e157506110cd57505080f35b9294927f040000000000000000000000000000000000000000000000000000000000000081036116055750509484958160011161160157600161157e92820191016128ce565b939091806001541690813b15611129578680946115ec8751988996879586947ff72f6a24000000000000000000000000000000000000000000000000000000008652169084016020909392919373ffffffffffffffffffffffffffffffffffffffff60408201951681520152565b03925af19081156110e157506110cd57505080f35b8580fd5b7f050000000000000000000000000000000000000000000000000000000000000081036116b65750509484958160011161160157600161164892820191016128ce565b939091806001541690813b15611129578680946115ec8751988996879586947fe0cc8c25000000000000000000000000000000000000000000000000000000008652169084016020909392919373ffffffffffffffffffffffffffffffffffffffff60408201951681520152565b7f0600000000000000000000000000000000000000000000000000000000000000810361177c575050948495816001116116015760016116f9928201910161289e565b90846001959495541692833b15611125576115ec85938a979388948a519b8c998a9889977f6d4211390000000000000000000000000000000000000000000000000000000089521692169086019092606092959493608083019673ffffffffffffffffffffffffffffffffffffffff809216845216602083015260408201520152565b7f07000000000000000000000000000000000000000000000000000000000000008103611842575050948495816001116116015760016117bf928201910161289e565b90846001959495541692833b15611125576115ec85938a979388948a519b8c998a9889977f7fd4e5400000000000000000000000000000000000000000000000000000000089521692169086019092606092959493608083019673ffffffffffffffffffffffffffffffffffffffff809216845216602083015260408201520152565b9192917f0800000000000000000000000000000000000000000000000000000000000000810361192d575050858596600111611601577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8385928101030112610b42576118bd60216118b660018501612364565b9301612364565b816001541690813b156111295784517ff1b2285500000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9484168516968101968752921692909216602085015292849184918290849082906040016115ec565b919492917f0900000000000000000000000000000000000000000000000000000000000000036119ed5786869760011161112957827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff918101030112610b425761199a6001859201612364565b16926001541692833b15610b42576024859283855196879485937f01681a620000000000000000000000000000000000000000000000000000000085528401525af19081156110e157506110cd57505080f35b8284517f48d3a3f7000000000000000000000000000000000000000000000000000000008152fd5b6024886032867f4e487b7100000000000000000000000000000000000000000000000000000000835252fd5b8386517ffeb51801000000000000000000000000000000000000000000000000000000008152fd5b5050503461016b57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261016b576020905173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b82818660607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261016b57611b0e612341565b92817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc3601126107875781517f313ce5670000000000000000000000000000000000000000000000000000000081529373ffffffffffffffffffffffffffffffffffffffff9081166020868481845afa958615611f01578596611ec5575b5083517f06fdde03000000000000000000000000000000000000000000000000000000009687825286828681865afa918215611ebb578792611e9f575b5085517f95d89b41000000000000000000000000000000000000000000000000000000009283825288828881885afa918215611e95579160209184938b92611e79575b50611c428a5194859384937fe8a0aed30000000000000000000000000000000000000000000000000000000085528c850161286b565b03818b897f0000000000000000000000000000000000000000000000000000000000000000165af1908115611e36579085918991611e5a575b5016978651908152878187818c5afa908115611e36578891611e40575b508651928352878387818c5afa928315611e3657602193888a9b94611d7d9794611d36948d611d6d9e95611e05575b5050611d23611cf260ff928551998a96602088019a8b5287015260a0606087015260c0860190612760565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09586868303016080870152612760565b911660a08301520390810184528361242e565b86519788927f020000000000000000000000000000000000000000000000000000000000000060208501525180928585019061273d565b810103600181018752018561242e565b6002541692833b15610b4257611dd093859284518096819482937f114357cb0000000000000000000000000000000000000000000000000000000084523390840152608060248401526084830190612760565b60243560448301526044356064830152039134905af19081156110e15750611df55750f35b611dfe906123fe565b6102bd5780f35b60ff929550611e2c611d2392611cf2923d8091833e611e24818361242e565b8101906127dd565b959250508e611cc7565b87513d8a823e3d90fd5b611e5491503d808a833e611e24818361242e565b8a611c98565b611e73915060203d60201161111057611102818361242e565b8b611c7b565b611e8e9192503d808d833e611e24818361242e565b908d611c0c565b88513d8b823e3d90fd5b611eb49192503d8089833e611e24818361242e565b9089611bc9565b86513d89823e3d90fd5b9095506020813d602011611ef9575b81611ee16020938361242e565b81010312610b4257611ef290612643565b9487611b8c565b3d9150611ed4565b84513d87823e3d90fd5b84807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102bd5763389a75e1600c523381526202a30042016020600c2055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d8280a280f35b5082847ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60808136011261016b5767ffffffffffffffff84358181116106d357611fc19036908701612385565b50506024359081116107875790608091360301126102bd57817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbc3601126102bd5750517f48d3a3f7000000000000000000000000000000000000000000000000000000008152fd5b8484917ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102bd57823567ffffffffffffffff811161016b576120729036908501612385565b505060807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc3601126102bd57817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff5c3601126102bd5750517f48d3a3f7000000000000000000000000000000000000000000000000000000008152fd5b909193925061010090817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261160157612128612341565b9136610104116111295761213b826123b3565b847fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7c36011261112957845193612170856123b3565b608435855260209460a435868201528352857fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff3c3601126107195785516121b5816123b3565b60c435815260e43586820152858401528551928584019533875273ffffffffffffffffffffffffffffffffffffffff80961688860152602435606086015260808501888b915b60028310612321575050505050825281019281841067ffffffffffffffff8511176122f557602184939261227692899688527f01000000000000000000000000000000000000000000000000000000000000006101208301526122668251809261012185019061273d565b810103600181018552018361242e565b60025416803b15610787576122c69484518096819482937f114357cb0000000000000000000000000000000000000000000000000000000084523390840152608060248401526084830190612760565b60443560448301526064356064830152039134905af19081156110e157506122ec575080f35b6110ca906123fe565b6024876041887f4e487b7100000000000000000000000000000000000000000000000000000000835252fd5b6001918491828751805183520151838201520194019101909289906121fb565b6004359073ffffffffffffffffffffffffffffffffffffffff8216820361028a57565b359073ffffffffffffffffffffffffffffffffffffffff8216820361028a57565b9181601f8401121561028a5782359167ffffffffffffffff831161028a576020838186019501011161028a57565b6040810190811067ffffffffffffffff8211176123cf57604052565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b67ffffffffffffffff81116123cf57604052565b60e0810190811067ffffffffffffffff8211176123cf57604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff8211176123cf57604052565b359060ff8216820361028a57565b6004359063ffffffff8216820361028a57565b67ffffffffffffffff81116123cf5760051b60200190565b81601f8201121561028a578035916124bf83612490565b926124cd604051948561242e565b808452602092838086019260051b82010192831161028a578301905b8282106124f7575050505090565b83809161250384612364565b8152019101906124e9565b81601f8201121561028a5780359161252583612490565b92612533604051948561242e565b808452602092838086019260051b82010192831161028a578301905b82821061255d575050505090565b8135815290830190830161254f565b90815180825260208080930193019160005b82811061258c575050505090565b835173ffffffffffffffffffffffffffffffffffffffff168552938101939281019260010161257e565b90815180825260208080930193019160005b8281106125d6575050505090565b8351855293810193928101926001016125c8565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffff7487392754330361261457565b6382b429006000526004601cfd5b519073ffffffffffffffffffffffffffffffffffffffff8216820361028a57565b519060ff8216820361028a57565b81601f8201121561028a5780519161266883612490565b92612676604051948561242e565b808452602092838086019260051b82010192831161028a578301905b8282106126a0575050505090565b8380916126ac84612622565b815201910190612692565b81601f8201121561028a578051916126ce83612490565b926126dc604051948561242e565b808452602092838086019260051b82010192831161028a578301905b828210612706575050505090565b815181529083019083016126f8565b919082604091031261028a5760405161272d816123b3565b6020808294803584520135910152565b60005b8381106127505750506000910152565b8181015183820152602001612740565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f60209361279c8151809281875287808801910161273d565b0116010190565b67ffffffffffffffff81116123cf57601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b60208183031261028a5780519067ffffffffffffffff821161028a570181601f8201121561028a578051612810816127a3565b9261281e604051948561242e565b8184526020828401011161028a5761283c916020808501910161273d565b90565b9081602091031261028a575173ffffffffffffffffffffffffffffffffffffffff8116810361028a5790565b9161289760ff91612889604094979697606087526060870190612760565b908582036020870152612760565b9416910152565b919082608091031261028a576128b382612364565b916128c060208201612364565b916060604083013592013590565b919082604091031261028a5760206128e583612364565b92013590565b81601f8201121561028a57803590612902826127a3565b92612910604051948561242e565b8284526020838301011161028a57816000926020809301838601378301015290565b9161296760209173ffffffffffffffffffffffffffffffffffffffff6060949796971685526080838601526080850190612760565b94805160408501520151910152565b9081602091031261028a5751801515810361028a579056fea164736f6c6343000813000a000000000000000000000000d7d8458823934233376d9ffb370a3293c4231860
Deployed Bytecode
0x608060408181526004918236101561001657600080fd5b60009260e08435811c9283630d9389ec146120ee5750826315859950146120295782631e2bcb1a14611f745782632569296214611f0b578483632786d29314611ad957508263448c8a2014611a6957826346fcff1814610e2957826351e372b014610cde578263546af93114610c8a57826354d1f13d14610c265782635abfaf6c14610bd2578263715018a614610b5257826383dd527d146108a557826384cb82b6146107fe5782638da5cb5b1461078b578263c4d66de81461052e578263cd9f6031146104da578263f04e283e14610412578263f2fde38b1461035d578263f4f79eb5146102c0578263fa536bec1461016f57505063fee81cf41461011b57600080fd5b3461016b5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261016b57602091610155612341565b9063389a75e1600c525281600c20549051908152f35b5080fd5b8484917ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102bd5767ffffffffffffffff90833582811161016b576101ba9036908601612385565b505060c07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc3601126102bd575081519060c082019081118282101761028f57825260243563ffffffff8116810361028a57815273ffffffffffffffffffffffffffffffffffffffff604435818116810361028a576020830152606435818116810361028a5783830152608435908116810361028a57606082015260a435608082015260a060c435910152517f48d3a3f7000000000000000000000000000000000000000000000000000000008152fd5b600080fd5b6041847f4e487b71000000000000000000000000000000000000000000000000000000006000525260246000fd5b80fd5b50828460607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102bd57823567ffffffffffffffff811161016b5761030b9036908501612385565b5050817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc3601126102bd5750517f48d3a3f7000000000000000000000000000000000000000000000000000000008152fd5b848260207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261016b57610391612341565b9061039a6125ea565b8160601b15610407575073ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffffffffffffffffffffffffffffffffffff748739278181547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a35580f35b637448fbae8352601cfd5b508360207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102bd57610446612341565b61044e6125ea565b63389a75e1600c528082526020600c2092835442116104cf57508173ffffffffffffffffffffffffffffffffffffffff929355167fffffffffffffffffffffffffffffffffffffffffffffffffffffffff748739278181547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a35580f35b636f5e88188352601cfd5b5050503461016b57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261016b5760209073ffffffffffffffffffffffffffffffffffffffff600254169051908152f35b5090503461078757602091827ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126106d35761056a612341565b916105736125ea565b73ffffffffffffffffffffffffffffffffffffffff80931693841561072e5761059a6125ea565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffff748739278181547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3557fffffffffffffffffffffffff0000000000000000000000000000000000000000948086600254161760025583517f5abfaf6c00000000000000000000000000000000000000000000000000000000815282818581855afa9081156107245783929187918a916106e7575b50168760015416176001558451938480927f546af9310000000000000000000000000000000000000000000000000000000082525afa9283156106de575085926106a4575b50501690600354161760035580f35b90809250813d83116106d7575b6106bb818361242e565b810103126106d3576106cc90612622565b3880610695565b8380fd5b503d6106b1565b513d87823e3d90fd5b938092508491503d831161071d575b610700818361242e565b8101031261071957856107138493612622565b38610650565b8780fd5b503d6106f6565b85513d8a823e3d90fd5b8060649351927f08c379a000000000000000000000000000000000000000000000000000000000845283015260248201527f427269646765204167656e7420616464726573732063616e6e6f7420626520306044820152fd5b8280fd5b5050503461016b57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261016b5760209073ffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffff7487392754915191168152f35b50828460807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102bd5761083361247d565b5060243567ffffffffffffffff811161016b576108539036908501612385565b5050817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbc3601126102bd5750517f48d3a3f7000000000000000000000000000000000000000000000000000000008152fd5b915092346102bd57602092837ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261016b579060c0916108e561247d565b9181516108f181612412565b81815281878201528183820152606094818680809401528260808201528260a082015201528063ffffffff602473ffffffffffffffffffffffffffffffffffffffff95866002541686519b8c9485937f83dd527d00000000000000000000000000000000000000000000000000000000855216908301525afa968715610b46578197610a2d575b6affffffffffffffffffffff87610a298a896109c58a8a8a805198888a998a5260ff885116818b01528701511681890152850151168187015283015182608087015261010086019061256c565b9060c0610a18610a056080860151947fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe095868a83030160a08b015261256c565b60a08601518589830301848a01526125b6565b9301519185840301908501526125b6565b0390f35b909196503d8083833e610a40818361242e565b810190868183031261078757805167ffffffffffffffff91828211610b42570186818403126106d357885193610a7585612412565b610a7e82612643565b8552888201516affffffffffffffffffffff8116810361016b5789860152610aa78a8301612622565b8a8601528682015183811161016b5784610ac2918401612651565b87860152608082015183811161016b5784610ade918401612651565b608086015260a082015183811161016b5784610afb9184016126b7565b60a086015260c08201519283116102bd575092610b306affffffffffffffffffffff999693610a299896936109c596016126b7565b60c08201529750929481949250610978565b8480fd5b509051903d90823e3d90fd5b84807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102bd57610b846125ea565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffff748739278181547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a35580f35b5050503461016b57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261016b5760209073ffffffffffffffffffffffffffffffffffffffff600154169051908152f35b84807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102bd5763389a75e1600c52338152806020600c2055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c928280a280f35b5050503461016b57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261016b5760209073ffffffffffffffffffffffffffffffffffffffff600354169051908152f35b8484917ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc9083823601126107875767ffffffffffffffff908535828111610b4257610d2c9036908801612385565b505060243592828411610b4257833603011261078757835192610d4e84612412565b610d5983870161246f565b8452610d6760248401612364565b6020850152604483013563ffffffff8116810361028a5785850152606483013582811161016b57610d9d908736918601016124a8565b6060850152608483013582811161016b57610dbd908736918601016124a8565b608085015260a483013582811161016b57610ddd9087369186010161250e565b60a085015260c48301359182116102bd575060c09185610e00923692010161250e565b910152517f48d3a3f7000000000000000000000000000000000000000000000000000000008152fd5b848285602093847ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126106d35767ffffffffffffffff92803584811161160157610e789036908301612385565b73ffffffffffffffffffffffffffffffffffffffff9081600354163303611a41578015611a155782357fff00000000000000000000000000000000000000000000000000000000000000167f0100000000000000000000000000000000000000000000000000000000000000810361112d575080600111610719578201937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff838603011261112957610f2c60018301612364565b602183013587811161112557856001610f47928601016128eb565b94604184013588811161112157610f648260018d938801016128eb565b610f89610f736061880161246f565b9360a1610f8260818a01612364565b9801612715565b97610fbf8a5194859384937fe8a0aed30000000000000000000000000000000000000000000000000000000085528b850161286b565b03818c877f000000000000000000000000d7d8458823934233376d9ffb370a3293c4231860165af1908115611117579083918a916110ea575b508188519b8c019316835216868a015285895260608901968988109088111761028f576021889961106d928989527f0300000000000000000000000000000000000000000000000000000000000000608083015261105d82518092608185019061273d565b810103600181018952018761242e565b806002541691823b15610719576110b488958751988996879586957f114357cb00000000000000000000000000000000000000000000000000000000875216908501612932565b039134905af19081156110e157506110cd575b50505b80f35b6110d6906123fe565b6102bd5780826110c7565b513d84823e3d90fd5b61110a91508b3d8d11611110575b611102818361242e565b81019061283f565b8b610ff8565b503d6110f8565b87513d8b823e3d90fd5b8980fd5b8880fd5b8680fd5b909193959798929496507f0200000000000000000000000000000000000000000000000000000000000000811460001461146f575080600111610719578301947fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8487030112611129576111a360018401612364565b946111b060218501612364565b956111bd60418601612364565b9084806111e56111cf60618a01612364565b9560a16111de60818c01612364565b9a01612715565b99169216918560015416938a517fd3fbcc2e000000000000000000000000000000000000000000000000000000008152828a8201528681602481895afa908115611465578d91611448575b5015611420578a517fae178a8f000000000000000000000000000000000000000000000000000000008152928716838a015260248301849052861660448301528490829060649082908e905af19283156113db5784918b946113e5575b50906024918a51928380927ffb6f948d0000000000000000000000000000000000000000000000000000000082528988168c8301525afa9081156113db578a916113ae575b50156113865760217f04000000000000000000000000000000000000000000000000000000000000009795938997959361105d61134661137261106d968f9e51928391878301968790602090939293604083019473ffffffffffffffffffffffffffffffffffffffff809216845216910152565b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0810183528261242e565b8a519b8c948501525180928585019061273d565b8588517f541ba3f8000000000000000000000000000000000000000000000000000000008152fd5b6113ce9150843d86116113d4575b6113c6818361242e565b810190612976565b8a6112d2565b503d6113bc565b89513d8c823e3d90fd5b8281939295503d8311611419575b6113fd818361242e565b810103126111215783611411602492612622565b93909161128d565b503d6113f3565b888b517fdbf8f2a1000000000000000000000000000000000000000000000000000000008152fd5b61145f9150873d89116113d4576113c6818361242e565b8d611230565b8c513d8f823e3d90fd5b90979694939295507f03000000000000000000000000000000000000000000000000000000000000008114600014611538575086869760011161112957827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff918101030112610b42576114e56001859201612364565b16926001541692833b15610b42576024859283855196879485937f56a43fde0000000000000000000000000000000000000000000000000000000085528401525af19081156110e157506110cd57505080f35b9294927f040000000000000000000000000000000000000000000000000000000000000081036116055750509484958160011161160157600161157e92820191016128ce565b939091806001541690813b15611129578680946115ec8751988996879586947ff72f6a24000000000000000000000000000000000000000000000000000000008652169084016020909392919373ffffffffffffffffffffffffffffffffffffffff60408201951681520152565b03925af19081156110e157506110cd57505080f35b8580fd5b7f050000000000000000000000000000000000000000000000000000000000000081036116b65750509484958160011161160157600161164892820191016128ce565b939091806001541690813b15611129578680946115ec8751988996879586947fe0cc8c25000000000000000000000000000000000000000000000000000000008652169084016020909392919373ffffffffffffffffffffffffffffffffffffffff60408201951681520152565b7f0600000000000000000000000000000000000000000000000000000000000000810361177c575050948495816001116116015760016116f9928201910161289e565b90846001959495541692833b15611125576115ec85938a979388948a519b8c998a9889977f6d4211390000000000000000000000000000000000000000000000000000000089521692169086019092606092959493608083019673ffffffffffffffffffffffffffffffffffffffff809216845216602083015260408201520152565b7f07000000000000000000000000000000000000000000000000000000000000008103611842575050948495816001116116015760016117bf928201910161289e565b90846001959495541692833b15611125576115ec85938a979388948a519b8c998a9889977f7fd4e5400000000000000000000000000000000000000000000000000000000089521692169086019092606092959493608083019673ffffffffffffffffffffffffffffffffffffffff809216845216602083015260408201520152565b9192917f0800000000000000000000000000000000000000000000000000000000000000810361192d575050858596600111611601577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8385928101030112610b42576118bd60216118b660018501612364565b9301612364565b816001541690813b156111295784517ff1b2285500000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9484168516968101968752921692909216602085015292849184918290849082906040016115ec565b919492917f0900000000000000000000000000000000000000000000000000000000000000036119ed5786869760011161112957827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff918101030112610b425761199a6001859201612364565b16926001541692833b15610b42576024859283855196879485937f01681a620000000000000000000000000000000000000000000000000000000085528401525af19081156110e157506110cd57505080f35b8284517f48d3a3f7000000000000000000000000000000000000000000000000000000008152fd5b6024886032867f4e487b7100000000000000000000000000000000000000000000000000000000835252fd5b8386517ffeb51801000000000000000000000000000000000000000000000000000000008152fd5b5050503461016b57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261016b576020905173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000d7d8458823934233376d9ffb370a3293c4231860168152f35b82818660607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261016b57611b0e612341565b92817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc3601126107875781517f313ce5670000000000000000000000000000000000000000000000000000000081529373ffffffffffffffffffffffffffffffffffffffff9081166020868481845afa958615611f01578596611ec5575b5083517f06fdde03000000000000000000000000000000000000000000000000000000009687825286828681865afa918215611ebb578792611e9f575b5085517f95d89b41000000000000000000000000000000000000000000000000000000009283825288828881885afa918215611e95579160209184938b92611e79575b50611c428a5194859384937fe8a0aed30000000000000000000000000000000000000000000000000000000085528c850161286b565b03818b897f000000000000000000000000d7d8458823934233376d9ffb370a3293c4231860165af1908115611e36579085918991611e5a575b5016978651908152878187818c5afa908115611e36578891611e40575b508651928352878387818c5afa928315611e3657602193888a9b94611d7d9794611d36948d611d6d9e95611e05575b5050611d23611cf260ff928551998a96602088019a8b5287015260a0606087015260c0860190612760565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09586868303016080870152612760565b911660a08301520390810184528361242e565b86519788927f020000000000000000000000000000000000000000000000000000000000000060208501525180928585019061273d565b810103600181018752018561242e565b6002541692833b15610b4257611dd093859284518096819482937f114357cb0000000000000000000000000000000000000000000000000000000084523390840152608060248401526084830190612760565b60243560448301526044356064830152039134905af19081156110e15750611df55750f35b611dfe906123fe565b6102bd5780f35b60ff929550611e2c611d2392611cf2923d8091833e611e24818361242e565b8101906127dd565b959250508e611cc7565b87513d8a823e3d90fd5b611e5491503d808a833e611e24818361242e565b8a611c98565b611e73915060203d60201161111057611102818361242e565b8b611c7b565b611e8e9192503d808d833e611e24818361242e565b908d611c0c565b88513d8b823e3d90fd5b611eb49192503d8089833e611e24818361242e565b9089611bc9565b86513d89823e3d90fd5b9095506020813d602011611ef9575b81611ee16020938361242e565b81010312610b4257611ef290612643565b9487611b8c565b3d9150611ed4565b84513d87823e3d90fd5b84807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102bd5763389a75e1600c523381526202a30042016020600c2055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d8280a280f35b5082847ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60808136011261016b5767ffffffffffffffff84358181116106d357611fc19036908701612385565b50506024359081116107875790608091360301126102bd57817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbc3601126102bd5750517f48d3a3f7000000000000000000000000000000000000000000000000000000008152fd5b8484917ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102bd57823567ffffffffffffffff811161016b576120729036908501612385565b505060807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc3601126102bd57817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff5c3601126102bd5750517f48d3a3f7000000000000000000000000000000000000000000000000000000008152fd5b909193925061010090817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261160157612128612341565b9136610104116111295761213b826123b3565b847fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7c36011261112957845193612170856123b3565b608435855260209460a435868201528352857fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff3c3601126107195785516121b5816123b3565b60c435815260e43586820152858401528551928584019533875273ffffffffffffffffffffffffffffffffffffffff80961688860152602435606086015260808501888b915b60028310612321575050505050825281019281841067ffffffffffffffff8511176122f557602184939261227692899688527f01000000000000000000000000000000000000000000000000000000000000006101208301526122668251809261012185019061273d565b810103600181018552018361242e565b60025416803b15610787576122c69484518096819482937f114357cb0000000000000000000000000000000000000000000000000000000084523390840152608060248401526084830190612760565b60443560448301526064356064830152039134905af19081156110e157506122ec575080f35b6110ca906123fe565b6024876041887f4e487b7100000000000000000000000000000000000000000000000000000000835252fd5b6001918491828751805183520151838201520194019101909289906121fb565b6004359073ffffffffffffffffffffffffffffffffffffffff8216820361028a57565b359073ffffffffffffffffffffffffffffffffffffffff8216820361028a57565b9181601f8401121561028a5782359167ffffffffffffffff831161028a576020838186019501011161028a57565b6040810190811067ffffffffffffffff8211176123cf57604052565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b67ffffffffffffffff81116123cf57604052565b60e0810190811067ffffffffffffffff8211176123cf57604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff8211176123cf57604052565b359060ff8216820361028a57565b6004359063ffffffff8216820361028a57565b67ffffffffffffffff81116123cf5760051b60200190565b81601f8201121561028a578035916124bf83612490565b926124cd604051948561242e565b808452602092838086019260051b82010192831161028a578301905b8282106124f7575050505090565b83809161250384612364565b8152019101906124e9565b81601f8201121561028a5780359161252583612490565b92612533604051948561242e565b808452602092838086019260051b82010192831161028a578301905b82821061255d575050505090565b8135815290830190830161254f565b90815180825260208080930193019160005b82811061258c575050505090565b835173ffffffffffffffffffffffffffffffffffffffff168552938101939281019260010161257e565b90815180825260208080930193019160005b8281106125d6575050505090565b8351855293810193928101926001016125c8565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffff7487392754330361261457565b6382b429006000526004601cfd5b519073ffffffffffffffffffffffffffffffffffffffff8216820361028a57565b519060ff8216820361028a57565b81601f8201121561028a5780519161266883612490565b92612676604051948561242e565b808452602092838086019260051b82010192831161028a578301905b8282106126a0575050505090565b8380916126ac84612622565b815201910190612692565b81601f8201121561028a578051916126ce83612490565b926126dc604051948561242e565b808452602092838086019260051b82010192831161028a578301905b828210612706575050505090565b815181529083019083016126f8565b919082604091031261028a5760405161272d816123b3565b6020808294803584520135910152565b60005b8381106127505750506000910152565b8181015183820152602001612740565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f60209361279c8151809281875287808801910161273d565b0116010190565b67ffffffffffffffff81116123cf57601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b60208183031261028a5780519067ffffffffffffffff821161028a570181601f8201121561028a578051612810816127a3565b9261281e604051948561242e565b8184526020828401011161028a5761283c916020808501910161273d565b90565b9081602091031261028a575173ffffffffffffffffffffffffffffffffffffffff8116810361028a5790565b9161289760ff91612889604094979697606087526060870190612760565b908582036020870152612760565b9416910152565b919082608091031261028a576128b382612364565b916128c060208201612364565b916060604083013592013590565b919082604091031261028a5760206128e583612364565b92013590565b81601f8201121561028a57803590612902826127a3565b92612910604051948561242e565b8284526020838301011161028a57816000926020809301838601378301015290565b9161296760209173ffffffffffffffffffffffffffffffffffffffff6060949796971685526080838601526080850190612760565b94805160408501520151910152565b9081602091031261028a5751801515810361028a579056fea164736f6c6343000813000a
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000d7d8458823934233376d9ffb370a3293c4231860
-----Decoded View---------------
Arg [0] : _hTokenFactoryAddress (address): 0xd7D8458823934233376d9fFb370A3293C4231860
-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 000000000000000000000000d7d8458823934233376d9ffb370a3293c4231860
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in POL
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.