Source Code
Overview
ETH Balance
ETH Value
$0.00| Transaction Hash |
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
Latest 25 internal transactions (View All)
Advanced mode:
| Parent Transaction Hash | Block | From | To | |||
|---|---|---|---|---|---|---|
| 26717077 | 47 days ago | 0 ETH | ||||
| 26717077 | 47 days ago | 0 ETH | ||||
| 26717077 | 47 days ago | 0 ETH | ||||
| 26717077 | 47 days ago | 0 ETH | ||||
| 26717077 | 47 days ago | 0 ETH | ||||
| 26717072 | 47 days ago | 0 ETH | ||||
| 26717072 | 47 days ago | 0 ETH | ||||
| 26717072 | 47 days ago | 0 ETH | ||||
| 26717072 | 47 days ago | 0 ETH | ||||
| 26717072 | 47 days ago | 0 ETH | ||||
| 26717072 | 47 days ago | 0 ETH | ||||
| 26717072 | 47 days ago | 0 ETH | ||||
| 26699519 | 48 days ago | 0 ETH | ||||
| 26699519 | 48 days ago | 0 ETH | ||||
| 26699519 | 48 days ago | 0 ETH | ||||
| 26699519 | 48 days ago | 0 ETH | ||||
| 26699519 | 48 days ago | 0 ETH | ||||
| 26699513 | 48 days ago | 0 ETH | ||||
| 26699513 | 48 days ago | 0 ETH | ||||
| 26699513 | 48 days ago | 0 ETH | ||||
| 26699513 | 48 days ago | 0 ETH | ||||
| 26699513 | 48 days ago | 0 ETH | ||||
| 26699513 | 48 days ago | 0 ETH | ||||
| 26699513 | 48 days ago | 0 ETH | ||||
| 26681842 | 48 days ago | 0 ETH |
Cross-Chain Transactions
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Name:
RebalancingManager
Compiler Version
v0.8.20+commit.a1b79de6
Optimization Enabled:
Yes with 999999 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.20;
import {Create2} from "@openzeppelin/contracts/utils/Create2.sol";
import {MessageHashUtils} from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol";
import {IEIP6492} from "../interfaces/IEIP6492.sol";
import {EncodeKeyLib} from "../lib/EncodeKeyLib.sol";
import {FeesLib} from "../lib/FeesLib.sol";
import {AccessControl} from "../manager/access/AccessControl.sol";
import {EIP3009Args} from "../manager/args/EIP3009Args.sol";
import {RebalancingArgs} from "../manager/args/RebalancingArgs.sol";
import {RebalancingConfigArgs} from "../manager/args/RebalancingConfigArgs.sol";
import {Operation} from "../manager/operation/Operation.sol";
import {PoolIdentifier} from "../manager/pool/PoolIdentifier.sol";
import {PoolToken} from "../manager/pool/PoolToken.sol";
import {IRebalancer} from "../rebalancer/IRebalancer.sol";
import {RebalancerProxy} from "../rebalancer/RebalancerProxy.sol";
import {IRebalancingManager} from "./IRebalancingManager.sol";
/**
* @title RebalancingManager
* @author MetaLend
* @notice This contract manages the rebalancing of assets across different pools and domains (chains).
*/
contract RebalancingManager is IRebalancingManager, AccessControl {
/// @notice The name type hash for EIP-712 signatures
bytes32 public constant NAME_TYPEHASH = keccak256(bytes("MetaLend Rebalancing"));
/// @notice The version type hash for EIP-712 signatures
bytes32 public constant VERSION_TYPEHASH = keccak256(bytes("3.0.0"));
/// @notice The domain type hash for EIP-712 signatures
bytes32 public constant DOMAIN_TYPEHASH =
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
/// @notice The withdrawal type hash for EIP-712 signatures
bytes32 public constant WITHDRAWAL_TYPEHASH =
keccak256("Withdrawal(address token,address poolContract,uint256 amount,uint256 deadline)");
/// @notice The denominator for the rebalancing fee percentage, used for basis points calculations
uint256 public constant FEE_DENOMINATOR = 100000;
/// @notice The maximum rebalancing fee percentage allowed
uint256 public constant MAX_REBALANCING_FEE_PERCENT = 1000; // 1%
/// @notice USDC CCTP domain ID for the Linea mainnet
uint32 public constant LINEA_DOMAIN_ID = 11;
/// @notice The address of the Linea AAVE USDC aToken contract
address private constant _LINEA_USDC_AAVE_TOKEN = 0x374D7860c4f2f604De0191298dD393703Cce84f3;
/// @notice The address of the rebalancer implementation contract
address private _rebalancerImplementation;
/// @notice The address of the USDC CCTP token messenger contract
address private _usdcCctpTokenMessenger;
/// @notice The address of the USDC CCTP message transmitter contract
address private _usdcCctpMessageTransmitter;
/// @notice The gas transaction fee for on behalf of transactions
/// @dev Discontinued storage slot in favor of _domainFees mapping supporting different fees per operation and different token decimals
uint256 private _gasTransactionFee;
/// @notice The address of the USDC token contract
address private _usdcToken;
/// @notice The cooldown period for rebalancing
uint256 public rebalanceCooldown;
/// @notice Mapping of owner addresses to rebalancer contract addresses
mapping(address owner => address rebalancer) public rebalancers;
/// @notice Mapping of destination domains, also specifies the fee on destination domain, supported is when value is greater than 0
/// @dev Discontinued storage slot in favor of _domainFees mapping supporting different fees per operation and different token decimals
// solhint-disable-next-line private-vars-leading-underscore
mapping(uint32 destinationDomain => uint256 supported) private supportedDestinationDomains;
/// @notice Mapping of rebalancer addresses to their last rebalance timestamps
/// @dev Discontinued storage slot in favor of _rebalancerLastRebalanceTimestampPerToken which supports cooldown per token
// solhint-disable-next-line private-vars-leading-underscore
mapping(address rebalancer => uint256 timestamp) private rebalancerLastRebalanceTimestamp;
/// @notice Mapping of token addresses to their corresponding pool contracts
/// @dev Discontinued storage slot in favor of _domainPoolContract mapping which supports multiple pool contracts for the same token
mapping(address token => mapping(PoolIdentifier pool => address poolContract)) private _tokenToPool;
/// @notice Mapping of used withdrawal signatures
mapping(bytes signature => bool used) private _usedWithdrawalSignatures;
/// @notice Mapping of pool contract addresses to their corresponding token address for each pool on given domain
/// @dev Discontinued storage slot in favor of _pools mapping which supports multiple tokens for the same pool address (such as AAVE)
mapping(uint256 destinationDomain => mapping(address poolContract => PoolToken poolToken)) private _domainPoolContract;
/// @notice The rebalancing fee percentage
uint256 private _rebalancingFeePercent;
/// @notice The destination domain for this chain
uint32 public thisDestinationDomain;
/// @notice The universal signature validator contract address
address public universalSigValidator;
/// @notice Mapping of domain to fees, additionally provides if domain is supported with value > 0 for given identifier (operation)
/// @dev for constants use fractional human readable format, convert constant by token decimals.
/// @dev enables future support of percentage based fees as well with a FeeType identifier included in encoded key
mapping(uint32 domain => mapping(bytes32 identifier => uint256 gasTransactionFee)) private _domainFees;
/// @notice Mapping of rebalancer address to token address to their last rebalance timestamp, supports cooldown per token
mapping(address rebalancer => mapping(address token => uint256 timestamp)) private _rebalancerLastRebalanceTimestampPerToken;
/// @notice Mapping domain to encoded supported pool (pool address, pool identifier, token) to boolean indicating if supported
mapping(uint256 domain => mapping(bytes32 pool => bool supported)) private _pools;
/**
* @inheritdoc IRebalancingManager
*/
function initialize(
address admin_,
address rebalancerImplementation_,
address tokenMessenger_,
address messageTransmitter_,
address usdcToken_,
uint32 thisDestinationDomain_,
address universalSigValidator_
) external override {
_initializeAdmin(admin_);
_rebalancerImplementation = rebalancerImplementation_;
_usdcCctpTokenMessenger = tokenMessenger_;
_usdcCctpMessageTransmitter = messageTransmitter_;
_usdcToken = usdcToken_;
thisDestinationDomain = thisDestinationDomain_;
universalSigValidator = universalSigValidator_;
}
/**
* @inheritdoc IRebalancingManager
*/
function depositWithAuthorization(
address token,
address onBehalfOf,
uint32 destinationDomain,
EIP3009Args calldata tranferArgs,
RebalancingArgs calldata rebalancingArgs,
RebalancingConfigArgs calldata rebalancingConfigArgs
) external override onlyOperator {
// 1. preprocess the deposit operation
address rebalancerAddress = _preprocessDeposit({
destinationDomain: destinationDomain,
rebalancingArgs: rebalancingArgs,
rebalancingConfigArgs: rebalancingConfigArgs,
onBehalfOf: onBehalfOf,
token: token
});
// 2. retrieve USDC approval signature components
(uint8 v, bytes32 r, bytes32 s) = _getSignatureComponents(onBehalfOf, tranferArgs.signature);
// 3. trigger deposit with authorization on the rebalancer
IRebalancer(rebalancerAddress).approveAndDeposit({
destinationDomain: destinationDomain == thisDestinationDomain ? type(uint32).max : destinationDomain,
pool: rebalancingArgs.pool,
poolContract: rebalancingArgs.poolContract,
token: token,
amount: tranferArgs.value,
validAfter: tranferArgs.validAfter,
validBefore: tranferArgs.validBefore,
nonce: tranferArgs.nonce,
v: v,
r: r,
s: s
});
// 4. finally, emit the DepositWithAuthorization event
emit DepositWithAuthorization(token, onBehalfOf, tranferArgs.value);
}
/**
* @inheritdoc IRebalancingManager
*/
function deposit(
address token,
address onBehalfOf,
uint32 destinationDomain,
RebalancingArgs calldata rebalancingArgs,
RebalancingConfigArgs calldata rebalancingConfigArgs
) external override onlyOperator {
// 1. preprocess the deposit operation
address rebalancerAddress = _preprocessDeposit({
destinationDomain: destinationDomain,
rebalancingArgs: rebalancingArgs,
rebalancingConfigArgs: rebalancingConfigArgs,
onBehalfOf: onBehalfOf,
token: token
});
// 2. trigger deposit on the rebalancer
uint256 amount = IRebalancer(rebalancerAddress).approveAndDeposit({
destinationDomain: destinationDomain == thisDestinationDomain ? type(uint32).max : destinationDomain,
pool: rebalancingArgs.pool,
poolContract: rebalancingArgs.poolContract,
token: token
});
// 3. finally, emit the Deposit event
emit Deposit(token, onBehalfOf, amount);
}
/**
* @inheritdoc IRebalancingManager
*/
function rebalanceUsdcSrc(
address onBehalfOf,
uint32 destinationDomain,
uint256 amount,
RebalancingArgs calldata rebalancingArgsFrom,
RebalancingArgs calldata rebalancingArgsTo,
RebalancingConfigArgs calldata rebalancingConfigArgs
) external override onlyOperator {
// 1. ensure the rebalancer exists for the owner
address rebalancerAddress = _getRebalancerEnsureCreated(onBehalfOf);
// 2. validation, domain must be supported, config must be valid, cooldown must be passed
_ensureDestinationDomainSupported(destinationDomain);
_ensurePoolContractSupported(thisDestinationDomain, rebalancingArgsFrom.poolContract, rebalancingArgsFrom.pool, _usdcToken);
// this ensures at least 1 pool is approved for the destination domain
_verifyPoolsApprovedAndSupported(rebalancingArgsTo, rebalancingConfigArgs, destinationDomain, _usdcToken);
_verifyConfigSignature(onBehalfOf, rebalancingConfigArgs);
_ensureCooldownPassedAndUpdate(rebalancerAddress, _usdcToken);
// 3. withdraw and deposit USDC for burn
IRebalancer(rebalancerAddress).withdrawAndDepositForBurnUsdc(
amount,
rebalancingArgsFrom.pool,
rebalancingArgsFrom.poolContract,
destinationDomain
);
// 4. finally, emit the RebalanceSrc event
emit RebalanceSrc(onBehalfOf, destinationDomain, rebalancingArgsFrom.pool, _usdcToken);
}
/**
* @inheritdoc IRebalancingManager
*/
function rebalanceUsdcDst(
address onBehalfOf,
RebalancingArgs calldata rebalancingArgs,
RebalancingConfigArgs calldata rebalancingConfigArgs,
bytes calldata message,
bytes calldata attestation
) external override onlyOperator {
// 1. validation, config must be valid
_verifyPoolsApprovedAndSupported(rebalancingArgs, rebalancingConfigArgs, thisDestinationDomain, _usdcToken);
_verifyConfigSignature(onBehalfOf, rebalancingConfigArgs);
// 2. get or create the rebalancer for the owner
address rebalancerAddress = _getOrCreateRebalancer(onBehalfOf);
// 3. claim the USDC tokens from the CCTP and deposit them into the pool
IRebalancer(rebalancerAddress).receiveUsdcApproveAndDeposit(
rebalancingArgs.pool,
rebalancingArgs.poolContract,
message,
attestation
);
// 4. finally, emit the RebalanceDst event
emit RebalanceDst(onBehalfOf, rebalancingArgs.pool, _usdcToken);
}
/**
* @inheritdoc IRebalancingManager
*/
function rebalanceThisDomain(
address onBehalfOf,
address token,
RebalancingArgs calldata rebalancingArgsFrom,
RebalancingArgs calldata rebalancingArgsTo,
RebalancingConfigArgs calldata rebalancingConfigArgs
) external override onlyOperator {
// 1. ensure the rebalancer exists for the owner
address rebalancerAddress = _getRebalancerEnsureCreated(onBehalfOf);
// 2. validation, config must be valid, cooldown must be passed, cannot rebalance to the same pool
if (rebalancingArgsFrom.poolContract == rebalancingArgsTo.poolContract) {
revert InvalidOperation();
}
_ensurePoolContractSupported(thisDestinationDomain, rebalancingArgsFrom.poolContract, rebalancingArgsFrom.pool, token);
_verifyPoolsApprovedAndSupported(rebalancingArgsTo, rebalancingConfigArgs, thisDestinationDomain, token);
_verifyConfigSignature(onBehalfOf, rebalancingConfigArgs);
_ensureCooldownPassedAndUpdate(rebalancerAddress, token);
// 3. withdraw from the source pool and deposit into the destination pool
IRebalancer(rebalancerAddress).withdrawAndRedeposit({
token: token,
srcPool: rebalancingArgsFrom.pool,
srcPoolContract: rebalancingArgsFrom.poolContract,
dstPool: rebalancingArgsTo.pool,
dstPoolContract: rebalancingArgsTo.poolContract
});
// 4. finally, emit the RebalanceSrc and RebalanceDst events
emit RebalanceSrc(onBehalfOf, thisDestinationDomain, rebalancingArgsFrom.pool, token);
emit RebalanceDst(onBehalfOf, rebalancingArgsTo.pool, token);
}
/**
* @inheritdoc IRebalancingManager
*/
function fundOwner(address onBehalfOf, RebalancingConfigArgs calldata rebalancingConfigArgs) external override onlyOperator {
// 1. validation, config must be valid, funding cap must be greater than zero, this domain must be Linea domain (11)
if (rebalancingConfigArgs.fundingCap == 0 || !isLineaDomain()) {
revert InvalidOperation();
}
_verifyConfigSignature(onBehalfOf, rebalancingConfigArgs);
// 2. ensure the rebalancer exists for the owner
address rebalancerAddress = _getRebalancerEnsureCreated(onBehalfOf);
// 3. fund the owner with aTokens (AAVE)
IRebalancer(rebalancerAddress).fundOwnerWithUsdcAaveToken(rebalancingConfigArgs.fundingCap);
}
/**
* @inheritdoc IRebalancingManager
*/
function withdrawFromPool(
address onBehalfOf,
address token,
PoolIdentifier pool,
address poolContract,
uint256 amount,
uint256 deadline,
bytes calldata signature
) external override {
address rebalancerAddress = _getRebalancerEnsureCreated(onBehalfOf);
_ensurePoolContractSupported(thisDestinationDomain, poolContract, pool, token);
// owner withdrawal
if (signature.length == 0) {
if (onBehalfOf != msg.sender) {
revert Unauthorized();
}
IRebalancer(rebalancerAddress).withdrawFromPoolToOwner({
token: token,
pool: pool,
poolContract: poolContract,
amount: amount,
payFee: false
});
} else {
// on behalf of withdrawal
_verifyWithdrawalSignature({
signer: onBehalfOf,
signature: signature,
token: token,
poolContract: poolContract,
amount: amount,
deadline: deadline
});
IRebalancer(rebalancerAddress).withdrawFromPoolToOwner({
token: token,
pool: pool,
poolContract: poolContract,
amount: amount,
payFee: true
});
}
}
/**
* @inheritdoc IRebalancingManager
*/
function claimRewardsMorpho(
address rewardDistributor,
address rewardToken,
uint256 claimable,
bytes32[] calldata proof
) external override {
address rebalancerAddress = _getOrCreateRebalancer(msg.sender);
IRebalancer(rebalancerAddress).claimRewardsMorpho(rewardDistributor, rewardToken, claimable, proof);
}
/**
* @inheritdoc IRebalancingManager
*/
function claimRewards(
address rewardDistributor,
address[] calldata rewardTokens,
uint256[] calldata claimable,
bytes32[][] calldata proofs
) external override {
address rebalancerAddress = _getOrCreateRebalancer(msg.sender);
IRebalancer(rebalancerAddress).claimRewards(rewardDistributor, rewardTokens, claimable, proofs);
}
/**
* @inheritdoc IRebalancingManager
*/
function claimRewardsAave(address rewardsController, address[] calldata assets) external override {
address rebalancerAddress = _getOrCreateRebalancer(msg.sender);
IRebalancer(rebalancerAddress).claimRewardsAave(rewardsController, assets);
}
/**
* @inheritdoc IRebalancingManager
*/
function updatePoolContract(
uint32 destinationDomain,
address[] calldata poolContracts,
PoolIdentifier[] calldata pools,
address[] calldata tokens,
bool[] calldata supported
) external override onlyAdmin {
if (poolContracts.length != pools.length || poolContracts.length != tokens.length || poolContracts.length != supported.length) {
revert InvalidInputLength();
}
uint256 length = poolContracts.length;
for (uint256 i = 0; i < length; ++i) {
bytes32 poolKey = EncodeKeyLib._getEncodedPoolKey(poolContracts[i], pools[i], tokens[i]);
_pools[destinationDomain][poolKey] = supported[i];
emit PoolContractUpdated({
destinationDomain: destinationDomain,
poolContract: poolContracts[i],
pool: pools[i],
token: tokens[i],
supported: supported[i]
});
}
}
/**
* @inheritdoc IRebalancingManager
*/
function updateRebalancerImplementation(address rebalancerImplementation) external override onlyAdmin {
_rebalancerImplementation = rebalancerImplementation;
emit RebalancerImplementationUpdated(rebalancerImplementation);
}
/**
* @inheritdoc IRebalancingManager
*/
function setDomainFeeForOperation(
uint32[] calldata domains,
Operation[] calldata operations,
uint256[] calldata fees
) external override onlyAdmin {
if (domains.length != operations.length || domains.length != fees.length) {
revert InvalidInputLength();
}
uint256 length = domains.length;
for (uint256 i = 0; i < length; ) {
uint32 domain = domains[i];
Operation operation = operations[i];
uint256 fee = fees[i];
if (domain == type(uint32).max) {
revert InvalidOperation();
}
_domainFees[domain][EncodeKeyLib._getEncodedOperationKey(operation)] = fee;
unchecked {
++i;
}
}
}
/**
* @inheritdoc IRebalancingManager
*/
function setRebalanceFeePercent(uint256 rebalancingFeePercent) external override onlyAdmin {
if (rebalancingFeePercent > MAX_REBALANCING_FEE_PERCENT) {
revert InvalidOperation();
}
_rebalancingFeePercent = rebalancingFeePercent;
emit RebalanceFeePercentUpdated(rebalancingFeePercent);
}
/**
* @inheritdoc IRebalancingManager
*/
function setRebalanceCooldown(uint256 cooldown) external override onlyAdmin {
rebalanceCooldown = cooldown;
}
/**
* @notice Returns the address of the rebalancer implementation contract
* @dev Use this with _implementation() in the proxy contract to get the implementation address
* @return rebalancerImplementation The address of the rebalancer implementation contract
*/
function getRebalancerImplementation() external view override returns (address rebalancerImplementation) {
rebalancerImplementation = _rebalancerImplementation;
}
/**
* @inheritdoc IRebalancingManager
*/
function getRebalanceFee(uint256 amount) external view override returns (uint256 rebalanceFee) {
if (_rebalancingFeePercent == 0 || amount == 0 || isLineaDomain()) {
return 0;
}
rebalanceFee = (amount * _rebalancingFeePercent) / FEE_DENOMINATOR;
}
/**
* @inheritdoc IRebalancingManager
*/
function getDestinationDomainGasTransactionFeeForOperation(
uint32 destinationDomain,
address token,
Operation operation,
bool useThisDomain
) external view override returns (uint256 gasTransactionFee) {
gasTransactionFee = FeesLib.convertConstantFeeToRawValue(
_domainFees[useThisDomain ? thisDestinationDomain : destinationDomain][EncodeKeyLib._getEncodedOperationKey(operation)],
token
);
}
/**
* @inheritdoc IRebalancingManager
*/
function getUsdcCctpTokenMessenger() external view override returns (address tokenMessenger) {
tokenMessenger = _usdcCctpTokenMessenger;
}
/**
* @inheritdoc IRebalancingManager
*/
function getUsdcCctpMessageTransmitter() external view override returns (address messageTransmitter) {
messageTransmitter = _usdcCctpMessageTransmitter;
}
/**
* @inheritdoc IRebalancingManager
*/
function getUsdcToken() external view override returns (address usdcToken) {
usdcToken = _usdcToken;
}
/**
* @inheritdoc IRebalancingManager
*/
function getPoolContractSupported(
uint32 destinationDomain,
address poolContract,
PoolIdentifier pool,
address token
) external view override returns (bool supported) {
supported = _pools[destinationDomain][EncodeKeyLib._getEncodedPoolKey(poolContract, pool, token)];
}
/**
* @inheritdoc IRebalancingManager
*/
function getLineaUsdcAaveToken() external pure override returns (address aToken) {
aToken = _LINEA_USDC_AAVE_TOKEN;
}
/**
* @inheritdoc IRebalancingManager
*/
function isLineaDomain() public view override returns (bool isLinea) {
isLinea = thisDestinationDomain == LINEA_DOMAIN_ID;
}
/**
* @notice Internal function to create or get the rebalancer address for a given owner
* @param owner The address of the owner
* @return rebalancerAddress The address of the rebalancer contract
*/
function _getOrCreateRebalancer(address owner) private returns (address rebalancerAddress) {
if (rebalancers[owner] == address(0)) {
rebalancerAddress = _createRebalancer(owner);
} else {
rebalancerAddress = rebalancers[owner];
}
}
/**
* @notice Internal function to create a new rebalancer contract
* @dev Reverts if the rebalancer already exists for the owner
* @param owner The address of the owner
* @return rebalancerAddress The address of the newly created rebalancer contract
*/
function _createRebalancer(address owner) private returns (address rebalancerAddress) {
bytes memory bytecode = type(RebalancerProxy).creationCode;
bytes memory initCode = abi.encodePacked(bytecode, abi.encode(address(this), owner));
bytes32 salt = keccak256(abi.encodePacked(owner));
rebalancerAddress = Create2.deploy(0, salt, initCode);
rebalancers[owner] = rebalancerAddress;
emit RebalancerDeployed(owner, rebalancerAddress);
}
/**
* @notice Internal function to preprocess the deposit operation
* @dev This function validates the pools, config and then gets or creates the rebalancer
* @param destinationDomain The destination domain for the deposit
* @param rebalancingArgs The rebalancing arguments. See `RebalancingArgs` struct for details.
* @param rebalancingConfigArgs The rebalancing configuration arguments. See `RebalancingConfigArgs` struct for details.
* @param onBehalfOf The address on behalf of which the deposit is made
* @param token The address of the token being deposited
* @return rebalancerAddress The address of the rebalancer contract
*/
function _preprocessDeposit(
uint32 destinationDomain,
RebalancingArgs calldata rebalancingArgs,
RebalancingConfigArgs calldata rebalancingConfigArgs,
address onBehalfOf,
address token
) private returns (address rebalancerAddress) {
// 1. validate approved pools and pool contracts and ensure the pool (vault) is supported for the token
_verifyPoolsApprovedAndSupported(rebalancingArgs, rebalancingConfigArgs, destinationDomain, token);
_verifyConfigSignature(onBehalfOf, rebalancingConfigArgs);
// 2. double check destination domain if different than this domain
if (destinationDomain != thisDestinationDomain) {
_ensureDestinationDomainSupported(destinationDomain);
// 2.1 only USDC is currently supported for cross-domain deposit
if (token != _usdcToken) {
revert InvalidOperation();
}
}
// 3. get or create the rebalancer for the owner
rebalancerAddress = _getOrCreateRebalancer(onBehalfOf);
// 4. reset cooldown to allow immediate rebalancing for new deposits
_resetCooldown(rebalancerAddress, token);
}
/**
* @notice Internal function to ensure the cooldown period has passed and update the last rebalance timestamp
* @param rebalancerAddress The address of the rebalancer contract
* @param token The address of the token for which to check the cooldown
*/
function _ensureCooldownPassedAndUpdate(address rebalancerAddress, address token) private {
uint256 lastRebalanceTimestamp = _rebalancerLastRebalanceTimestampPerToken[rebalancerAddress][token];
if (block.timestamp < lastRebalanceTimestamp + rebalanceCooldown) {
revert RebalanceCooldownNotPassed(block.timestamp, lastRebalanceTimestamp + rebalanceCooldown);
}
_rebalancerLastRebalanceTimestampPerToken[rebalancerAddress][token] = block.timestamp;
emit RebalancerLastRebalanceTimestampUpdated(rebalancerAddress, token, block.timestamp);
}
/**
* @notice Internal function to reset the cooldown for a rebalancer
* @dev This function sets the last rebalance timestamp to zero, effectively resetting the cooldown
* @param rebalancerAddress The address of the rebalancer contract
* @param token The address of the token for which to reset the cooldown
*/
function _resetCooldown(address rebalancerAddress, address token) private {
// Only reset if cooldown has NOT passed yet to avoid unnecessary storage writes
if (block.timestamp < _rebalancerLastRebalanceTimestampPerToken[rebalancerAddress][token] + rebalanceCooldown) {
_rebalancerLastRebalanceTimestampPerToken[rebalancerAddress][token] = 0;
emit RebalancerLastRebalanceTimestampUpdated(rebalancerAddress, token, 0);
}
}
/**
* @notice Internal function to verify the withdrawal signature
* @dev Reverts if the signature is invalid. The message hash is created by hashing the token address, amount and chain ID.
* @param signer The address of the signer
* @param signature The signature to verify
* @param token The address of the token
* @param poolContract The address of the pool contract
* @param amount The amount to verify against
* @param deadline The deadline for the signature
*/
function _verifyWithdrawalSignature(
address signer,
bytes memory signature,
address token,
address poolContract,
uint256 amount,
uint256 deadline
) private {
if (block.timestamp > deadline) {
revert InvalidDeadline(deadline);
}
if (_usedWithdrawalSignatures[signature]) {
revert SignatureUsedAlready(signature);
}
_usedWithdrawalSignatures[signature] = true;
bytes32 structHash = keccak256(abi.encode(WITHDRAWAL_TYPEHASH, token, poolContract, amount, deadline));
bytes32 digest = keccak256(abi.encodePacked("\x19\x01", _getDomainSeparator(), structHash));
_verifySignature(signer, signature, digest);
}
/**
* @notice Internal function to verify the signature of the rebalancing configuration
* @dev Reverts if the signature is invalid. The message hash is created by hashing the approved pools, pool contracts, and domains.
* @param signer The address of the signer
* @param rebalancingConfigArgs The rebalancing configuration arguments. See `RebalancingConfigArgs` struct for details.
*/
function _verifyConfigSignature(address signer, RebalancingConfigArgs calldata rebalancingConfigArgs) private {
bytes32 messageHash = rebalancingConfigArgs.fundingCap == 0
? keccak256(
abi.encode(
rebalancingConfigArgs.approvedPools,
rebalancingConfigArgs.approvedPoolContracts,
rebalancingConfigArgs.approvedDestinationDomains
)
)
: keccak256(
abi.encode(
rebalancingConfigArgs.approvedPools,
rebalancingConfigArgs.approvedPoolContracts,
rebalancingConfigArgs.approvedDestinationDomains,
rebalancingConfigArgs.fundingCap
)
);
bytes32 ethSignedMessageHash = MessageHashUtils.toEthSignedMessageHash(messageHash);
_verifySignature(signer, rebalancingConfigArgs.signature, ethSignedMessageHash);
}
/**
* @notice Internal helper function to verify the signature
* @dev Reverts if the signature is invalid.
* @param signer The address of the signer
* @param signature The signature to verify
* @param digest The digest to verify against
*/
function _verifySignature(address signer, bytes memory signature, bytes32 digest) private {
if (!IEIP6492(universalSigValidator).isValidSig(signer, digest, signature)) {
revert InvalidSignature(signer, signature);
}
}
/**
* @notice Internal function to get the EIP-712 domain separator
* @dev This function constructs the domain separator used for EIP-712 signatures.
* @return domainSeparator The EIP-712 domain separator
*/
function _getDomainSeparator() private view returns (bytes32 domainSeparator) {
domainSeparator = keccak256(abi.encode(DOMAIN_TYPEHASH, NAME_TYPEHASH, VERSION_TYPEHASH, block.chainid, address(this)));
}
/**
* @notice Internal function to ensure the rebalancer exists for the given owner
* @dev Reverts if the rebalancer does not exist
* @param owner The address of the owner
* @return rebalancerAddress The address of the rebalancer contract
*/
function _getRebalancerEnsureCreated(address owner) private view returns (address rebalancerAddress) {
rebalancerAddress = rebalancers[owner];
if (rebalancerAddress == address(0)) {
revert RebalancerNotFound(owner);
}
}
/**
* @notice Internal function to ensure the destination domain is supported
* @dev Reverts if the destination domain is not supported
* @param destinationDomain The destination domain to check
*/
function _ensureDestinationDomainSupported(uint32 destinationDomain) private view {
if (
destinationDomain == thisDestinationDomain ||
_domainFees[destinationDomain][EncodeKeyLib._getEncodedOperationKey(Operation.REBALANCE_DST_DOMAIN)] == 0
) {
revert DestinationDomainNotSupported(destinationDomain);
}
}
/**
* @notice Internal function to ensure the pool contract is supported
* @dev Reverts if the pool contract is not supported
* @param destinationDomain The destination domain to check
* @param poolContract The address of the pool contract to check
* @param expectedPool The expected pool identifier for the contract
* @param expectedToken The expected token address for the contract
*/
function _ensurePoolContractSupported(
uint32 destinationDomain,
address poolContract,
PoolIdentifier expectedPool,
address expectedToken
) private view {
bool isSupported = _pools[destinationDomain][EncodeKeyLib._getEncodedPoolKey(poolContract, expectedPool, expectedToken)];
if (!isSupported) {
revert PoolContractNotSupported(poolContract);
}
}
/**
* @notice Internal function to verify the approved pools and pool contracts
* @dev Reverts if any of the following conditions are not met:
* 1. The pool must be approved in the rebalancing arguments.
* 2. The pool contract must be approved for the pool in the rebalancing arguments.
* 3. The destination domain must be approved in the rebalancing arguments.
* 4. The pool contract must be supported for the token.
* @param rebalancingArgs The rebalancing arguments. See `RebalancingArgs` struct for details.
* @param rebalancingConfigArgs The rebalancing configuration arguments. See `RebalancingConfigArgs` struct for details.
* @param destinationDomain The destination domain to verify against
* @param token The token address to verify against
*/
function _verifyPoolsApprovedAndSupported(
RebalancingArgs calldata rebalancingArgs,
RebalancingConfigArgs calldata rebalancingConfigArgs,
uint32 destinationDomain,
address token
) private view {
// 1. validate approved pools and pool contracts
_verifyConfig(rebalancingArgs, rebalancingConfigArgs, destinationDomain);
// 2. ensure the pool (vault) is supported for the token
_ensurePoolContractSupported(destinationDomain, rebalancingArgs.poolContract, rebalancingArgs.pool, token);
}
/**
* @notice Internal function to verify the rebalancing configuration
* @dev Reverts if any of the following conditions are not met:
* 1. The pool must be approved in the rebalancing arguments.
* 2. The pool contract must be approved for the pool in the rebalancing arguments.
* 3. The destination domain must be approved in the rebalancing arguments.
* @param rebalancingArgs The rebalancing arguments. See `RebalancingArgs` struct for details.
* @param rebalancingConfigArgs The rebalancing configuration arguments. See `RebalancingConfigArgs` struct for details.
* @param destinationDomain The destination domain to verify against
*/
function _verifyConfig(
RebalancingArgs calldata rebalancingArgs,
RebalancingConfigArgs calldata rebalancingConfigArgs,
uint32 destinationDomain
) private pure {
if (
rebalancingConfigArgs.approvedPools.length != rebalancingConfigArgs.approvedPoolContracts.length ||
rebalancingConfigArgs.approvedPools.length != rebalancingConfigArgs.approvedDestinationDomains.length
) {
revert InvalidInputLength();
}
uint256 length = rebalancingConfigArgs.approvedPools.length;
for (uint256 i = 0; i < length; ) {
PoolIdentifier pool = rebalancingConfigArgs.approvedPools[i];
address poolContract = rebalancingConfigArgs.approvedPoolContracts[i];
uint32 domain = rebalancingConfigArgs.approvedDestinationDomains[i];
if (pool == rebalancingArgs.pool && poolContract == rebalancingArgs.poolContract && domain == destinationDomain) {
return;
}
unchecked {
++i;
}
}
revert InvalidOperation();
}
/**
* @notice Internal function to get the signature components from the signature
* @param signer The address of the signer
* @param signature The signature to extract components from
* @return v The v component of the signature
* @return r The r component of the signature
* @return s The s component of the signature
*/
function _getSignatureComponents(address signer, bytes memory signature) private pure returns (uint8 v, bytes32 r, bytes32 s) {
if (signature.length != 65) {
revert InvalidSignature(signer, signature);
}
assembly ("memory-safe") {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/Proxy.sol)
pragma solidity ^0.8.20;
/**
* @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM
* instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to
* be specified by overriding the virtual {_implementation} function.
*
* Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a
* different contract through the {_delegate} function.
*
* The success and return data of the delegated call will be returned back to the caller of the proxy.
*/
abstract contract Proxy {
/**
* @dev Delegates the current call to `implementation`.
*
* This function does not return to its internal call site, it will return directly to the external caller.
*/
function _delegate(address implementation) internal virtual {
assembly {
// Copy msg.data. We take full control of memory in this inline assembly
// block because it will not return to Solidity code. We overwrite the
// Solidity scratch pad at memory position 0.
calldatacopy(0, 0, calldatasize())
// Call the implementation.
// out and outsize are 0 because we don't know the size yet.
let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)
// Copy the returned data.
returndatacopy(0, 0, returndatasize())
switch result
// delegatecall returns 0 on error.
case 0 {
revert(0, returndatasize())
}
default {
return(0, returndatasize())
}
}
}
/**
* @dev This is a virtual function that should be overridden so it returns the address to which the fallback
* function and {_fallback} should delegate.
*/
function _implementation() internal view virtual returns (address);
/**
* @dev Delegates the current call to the address returned by `_implementation()`.
*
* This function does not return to its internal call site, it will return directly to the external caller.
*/
function _fallback() internal virtual {
_delegate(_implementation());
}
/**
* @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other
* function in the contract matches the call data.
*/
fallback() external payable virtual {
_fallback();
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Create2.sol)
pragma solidity ^0.8.20;
/**
* @dev Helper to make usage of the `CREATE2` EVM opcode easier and safer.
* `CREATE2` can be used to compute in advance the address where a smart
* contract will be deployed, which allows for interesting new mechanisms known
* as 'counterfactual interactions'.
*
* See the https://eips.ethereum.org/EIPS/eip-1014#motivation[EIP] for more
* information.
*/
library Create2 {
/**
* @dev Not enough balance for performing a CREATE2 deploy.
*/
error Create2InsufficientBalance(uint256 balance, uint256 needed);
/**
* @dev There's no code to deploy.
*/
error Create2EmptyBytecode();
/**
* @dev The deployment failed.
*/
error Create2FailedDeployment();
/**
* @dev Deploys a contract using `CREATE2`. The address where the contract
* will be deployed can be known in advance via {computeAddress}.
*
* The bytecode for a contract can be obtained from Solidity with
* `type(contractName).creationCode`.
*
* Requirements:
*
* - `bytecode` must not be empty.
* - `salt` must have not been used for `bytecode` already.
* - the factory must have a balance of at least `amount`.
* - if `amount` is non-zero, `bytecode` must have a `payable` constructor.
*/
function deploy(uint256 amount, bytes32 salt, bytes memory bytecode) internal returns (address addr) {
if (address(this).balance < amount) {
revert Create2InsufficientBalance(address(this).balance, amount);
}
if (bytecode.length == 0) {
revert Create2EmptyBytecode();
}
/// @solidity memory-safe-assembly
assembly {
addr := create2(amount, add(bytecode, 0x20), mload(bytecode), salt)
}
if (addr == address(0)) {
revert Create2FailedDeployment();
}
}
/**
* @dev Returns the address where a contract will be stored if deployed via {deploy}. Any change in the
* `bytecodeHash` or `salt` will result in a new destination address.
*/
function computeAddress(bytes32 salt, bytes32 bytecodeHash) internal view returns (address) {
return computeAddress(salt, bytecodeHash, address(this));
}
/**
* @dev Returns the address where a contract will be stored if deployed via {deploy} from a contract located at
* `deployer`. If `deployer` is this contract's address, returns the same value as {computeAddress}.
*/
function computeAddress(bytes32 salt, bytes32 bytecodeHash, address deployer) internal pure returns (address addr) {
/// @solidity memory-safe-assembly
assembly {
let ptr := mload(0x40) // Get free memory pointer
// | | ↓ ptr ... ↓ ptr + 0x0B (start) ... ↓ ptr + 0x20 ... ↓ ptr + 0x40 ... |
// |-------------------|---------------------------------------------------------------------------|
// | bytecodeHash | CCCCCCCCCCCCC...CC |
// | salt | BBBBBBBBBBBBB...BB |
// | deployer | 000000...0000AAAAAAAAAAAAAAAAAAA...AA |
// | 0xFF | FF |
// |-------------------|---------------------------------------------------------------------------|
// | memory | 000000...00FFAAAAAAAAAAAAAAAAAAA...AABBBBBBBBBBBBB...BBCCCCCCCCCCCCC...CC |
// | keccak(start, 85) | ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ |
mstore(add(ptr, 0x40), bytecodeHash)
mstore(add(ptr, 0x20), salt)
mstore(ptr, deployer) // Right-aligned with 12 preceding garbage bytes
let start := add(ptr, 0x0b) // The hashed data starts at the final garbage byte which we will set to 0xff
mstore8(start, 0xff)
addr := keccak256(start, 85)
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/MessageHashUtils.sol)
pragma solidity ^0.8.20;
import {Strings} from "../Strings.sol";
/**
* @dev Signature message hash utilities for producing digests to be consumed by {ECDSA} recovery or signing.
*
* The library provides methods for generating a hash of a message that conforms to the
* https://eips.ethereum.org/EIPS/eip-191[EIP 191] and https://eips.ethereum.org/EIPS/eip-712[EIP 712]
* specifications.
*/
library MessageHashUtils {
/**
* @dev Returns the keccak256 digest of an EIP-191 signed data with version
* `0x45` (`personal_sign` messages).
*
* The digest is calculated by prefixing a bytes32 `messageHash` with
* `"\x19Ethereum Signed Message:\n32"` and hashing the result. It corresponds with the
* hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method.
*
* NOTE: The `messageHash` parameter is intended to be the result of hashing a raw message with
* keccak256, although any bytes32 value can be safely used because the final digest will
* be re-hashed.
*
* See {ECDSA-recover}.
*/
function toEthSignedMessageHash(bytes32 messageHash) internal pure returns (bytes32 digest) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, "\x19Ethereum Signed Message:\n32") // 32 is the bytes-length of messageHash
mstore(0x1c, messageHash) // 0x1c (28) is the length of the prefix
digest := keccak256(0x00, 0x3c) // 0x3c is the length of the prefix (0x1c) + messageHash (0x20)
}
}
/**
* @dev Returns the keccak256 digest of an EIP-191 signed data with version
* `0x45` (`personal_sign` messages).
*
* The digest is calculated by prefixing an arbitrary `message` with
* `"\x19Ethereum Signed Message:\n" + len(message)` and hashing the result. It corresponds with the
* hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method.
*
* See {ECDSA-recover}.
*/
function toEthSignedMessageHash(bytes memory message) internal pure returns (bytes32) {
return
keccak256(bytes.concat("\x19Ethereum Signed Message:\n", bytes(Strings.toString(message.length)), message));
}
/**
* @dev Returns the keccak256 digest of an EIP-191 signed data with version
* `0x00` (data with intended validator).
*
* The digest is calculated by prefixing an arbitrary `data` with `"\x19\x00"` and the intended
* `validator` address. Then hashing the result.
*
* See {ECDSA-recover}.
*/
function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) {
return keccak256(abi.encodePacked(hex"19_00", validator, data));
}
/**
* @dev Returns the keccak256 digest of an EIP-712 typed data (EIP-191 version `0x01`).
*
* The digest is calculated from a `domainSeparator` and a `structHash`, by prefixing them with
* `\x19\x01` and hashing the result. It corresponds to the hash signed by the
* https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] JSON-RPC method as part of EIP-712.
*
* See {ECDSA-recover}.
*/
function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 digest) {
/// @solidity memory-safe-assembly
assembly {
let ptr := mload(0x40)
mstore(ptr, hex"19_01")
mstore(add(ptr, 0x02), domainSeparator)
mstore(add(ptr, 0x22), structHash)
digest := keccak256(ptr, 0x42)
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
/**
* @dev Muldiv operation overflow.
*/
error MathOverflowedMulDiv();
enum Rounding {
Floor, // Toward negative infinity
Ceil, // Toward positive infinity
Trunc, // Toward zero
Expand // Away from zero
}
/**
* @dev Returns the addition of two unsigned integers, with an overflow flag.
*/
function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the subtraction of two unsigned integers, with an overflow flag.
*/
function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b > a) return (false, 0);
return (true, a - b);
}
}
/**
* @dev Returns the multiplication of two unsigned integers, with an overflow flag.
*/
function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the division of two unsigned integers, with a division by zero flag.
*/
function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a / b);
}
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
*/
function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a % b);
}
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds towards infinity instead
* of rounding towards zero.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
if (b == 0) {
// Guarantee the same behavior as in a regular Solidity division.
return a / b;
}
// (a + b - 1) / b can overflow on addition, so we distribute.
return a == 0 ? 0 : (a - 1) / b + 1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
* denominator == 0.
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
* Uniswap Labs also under MIT license.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2^256 + prod0.
uint256 prod0 = x * y; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
// Solidity will revert if denominator == 0, unlike the div opcode on its own.
// The surrounding unchecked block does not change this fact.
// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
if (denominator <= prod1) {
revert MathOverflowedMulDiv();
}
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator.
// Always >= 1. See https://cs.stackexchange.com/q/138556/92363.
uint256 twos = denominator & (0 - denominator);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 2^4.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also
// works in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // inverse mod 2^256
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
* towards zero.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
//
// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
//
// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
//
// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
uint256 result = 1 << (log2(a) >> 1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
// into the expected uint128 result.
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256 of a positive value rounded towards zero.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 256, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);
}
}
/**
* @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
*/
function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
return uint8(rounding) % 2 == 1;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SignedMath.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard signed math utilities missing in the Solidity language.
*/
library SignedMath {
/**
* @dev Returns the largest of two signed numbers.
*/
function max(int256 a, int256 b) internal pure returns (int256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two signed numbers.
*/
function min(int256 a, int256 b) internal pure returns (int256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two signed numbers without overflow.
* The result is rounded towards zero.
*/
function average(int256 a, int256 b) internal pure returns (int256) {
// Formula from the book "Hacker's Delight"
int256 x = (a & b) + ((a ^ b) >> 1);
return x + (int256(uint256(x) >> 255) & (a ^ b));
}
/**
* @dev Returns the absolute unsigned value of a signed value.
*/
function abs(int256 n) internal pure returns (uint256) {
unchecked {
// must be unchecked in order to support `n = type(int256).min`
return uint256(n >= 0 ? n : -n);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Strings.sol)
pragma solidity ^0.8.20;
import {Math} from "./math/Math.sol";
import {SignedMath} from "./math/SignedMath.sol";
/**
* @dev String operations.
*/
library Strings {
bytes16 private constant HEX_DIGITS = "0123456789abcdef";
uint8 private constant ADDRESS_LENGTH = 20;
/**
* @dev The `value` string doesn't fit in the specified `length`.
*/
error StringsInsufficientHexLength(uint256 value, uint256 length);
/**
* @dev Converts a `uint256` to its ASCII `string` decimal representation.
*/
function toString(uint256 value) internal pure returns (string memory) {
unchecked {
uint256 length = Math.log10(value) + 1;
string memory buffer = new string(length);
uint256 ptr;
/// @solidity memory-safe-assembly
assembly {
ptr := add(buffer, add(32, length))
}
while (true) {
ptr--;
/// @solidity memory-safe-assembly
assembly {
mstore8(ptr, byte(mod(value, 10), HEX_DIGITS))
}
value /= 10;
if (value == 0) break;
}
return buffer;
}
}
/**
* @dev Converts a `int256` to its ASCII `string` decimal representation.
*/
function toStringSigned(int256 value) internal pure returns (string memory) {
return string.concat(value < 0 ? "-" : "", toString(SignedMath.abs(value)));
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
*/
function toHexString(uint256 value) internal pure returns (string memory) {
unchecked {
return toHexString(value, Math.log256(value) + 1);
}
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
*/
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
uint256 localValue = value;
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = HEX_DIGITS[localValue & 0xf];
localValue >>= 4;
}
if (localValue != 0) {
revert StringsInsufficientHexLength(value, length);
}
return string(buffer);
}
/**
* @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal
* representation.
*/
function toHexString(address addr) internal pure returns (string memory) {
return toHexString(uint256(uint160(addr)), ADDRESS_LENGTH);
}
/**
* @dev Returns true if the two strings are equal.
*/
function equal(string memory a, string memory b) internal pure returns (bool) {
return bytes(a).length == bytes(b).length && keccak256(bytes(a)) == keccak256(bytes(b));
}
}// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.20;
/**
* @title IEIP6492
* @author MetaLend
* @notice Minimal interface for EIP-6492 signature validation.
* @dev This interface allows for the validation of signatures according to EIP-6492.
*/
interface IEIP6492 {
/**
* @notice Validates a signature against a given hash.
* @dev This function can revert if the underlying call reverts.
* @param _signer The address of the signer.
* @param _hash The hash to validate against.
* @param _signature The signature to validate.
* @return bool Returns true if the signature is valid, false otherwise.
*/
function isValidSig(address _signer, bytes32 _hash, bytes calldata _signature) external returns (bool);
}// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.20;
import {Operation} from "../manager/operation/Operation.sol";
import {PoolIdentifier} from "../manager/pool/PoolIdentifier.sol";
/**
* @title EncodeKeyLib
* @author MetaLend
* @notice Library for encoding keys used in mappings
* @dev This library provides functions to encode keys for mapping bytes32 identifiers
*/
library EncodeKeyLib {
/**
* @notice External function to get the encoded pool key
* @param poolContract The address of the pool contract
* @param pool The pool identifier
* @param token The address of the token
* @return encodedKey The encoded pool key
*/
function _getEncodedPoolKey(address poolContract, PoolIdentifier pool, address token) internal pure returns (bytes32 encodedKey) {
encodedKey = keccak256(abi.encodePacked(poolContract, pool, token));
}
/**
* @notice External function to get the encoded operation key
* @param operation The operation type
* @return encodedKey The encoded operation key
*/
function _getEncodedOperationKey(Operation operation) internal pure returns (bytes32 encodedKey) {
encodedKey = keccak256(abi.encodePacked(operation));
}
}// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.20;
/**
* @title IERC20
* @author MetaLend
* @notice Minimal interface for ERC20 tokens to get decimals
* @dev This interface is used to interact with ERC20 tokens to retrieve their decimal places
*/
interface IERC20 {
function decimals() external view returns (uint8);
}
/**
* @title FeesLib
* @author MetaLend
* @notice Library for managing fees
* @dev This library provides extension helper functions for fee management
*/
library FeesLib {
uint256 private constant _DENOMINATOR = 1000; // supports milli-tokens (0.001)
/**
* @notice Convert a fractional human-readable fee to raw token units
* @param constantFee The numerator for the fee (e.g. `1` if fee = 0.001 tokens with denominator = 1000)
* @param token The ERC20 token address
* @return rawValue The fee converted into token smallest units
*/
function convertConstantFeeToRawValue(uint256 constantFee, address token) external view returns (uint256 rawValue) {
uint8 decimals = IERC20(token).decimals();
uint256 base = 10 ** decimals;
// Scale fee proportionally to token decimals
rawValue = (constantFee * base) / _DENOMINATOR;
}
}// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.20;
import {IAccessControl} from "./IAccessControl.sol";
/**
* @title AccessControl
* @author MetaLend
* @notice Contract module that provides access control mechanisms
*/
abstract contract AccessControl is IAccessControl {
/// @notice Operator role identifier
bytes32 private constant _OPERATOR_ROLE = keccak256("OPERATOR_ROLE");
/// @notice Admin address
address private _admin;
/// @notice Operator role mapping
mapping(address account => bytes32 role) public roles;
/// @notice Modifier to restrict access to admin
modifier onlyAdmin() {
if (msg.sender != _admin) revert Unauthorized();
_;
}
/// @notice Modifier to restrict access to operator
modifier onlyOperator() {
if (roles[msg.sender] != _OPERATOR_ROLE) revert Unauthorized();
_;
}
/**
* @inheritdoc IAccessControl
*/
function setAdmin(address newAdmin) external override onlyAdmin {
require(newAdmin != address(0), "AccessControl: new admin is the zero address");
address oldAdmin = _admin;
_admin = newAdmin;
emit AdminChanged(oldAdmin, newAdmin);
}
/**
* @inheritdoc IAccessControl
*/
function addOperator(address operator) external override onlyAdmin {
require(operator != address(0), "AccessControl: operator is the zero address");
roles[operator] = _OPERATOR_ROLE;
emit OperatorAdded(operator);
}
/**
* @inheritdoc IAccessControl
*/
function removeOperator(address operator) external override onlyAdmin {
delete roles[operator];
emit OperatorRemoved(operator);
}
/**
* @inheritdoc IAccessControl
*/
function getAdmin() external view override returns (address admin) {
admin = _admin;
}
/**
* @inheritdoc IAccessControl
*/
function isOperator(address operator) external view override returns (bool isOpr) {
isOpr = roles[operator] == _OPERATOR_ROLE;
}
/**
* @notice Initialize the admin address
* @param admin_ The address of the admin
*/
function _initializeAdmin(address admin_) internal {
require(_admin == address(0), "AccessControl: admin already initialized");
require(admin_ != address(0), "AccessControl: admin is the zero address");
_admin = admin_;
}
}// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.20;
/**
* @title IAccessControl
* @author MetaLend
* @notice Interface for access control mechanisms
*/
interface IAccessControl {
/**
* @notice Emitted when admin is changed
* @param oldAdmin Previous admin address
* @param newAdmin New admin address
*/
event AdminChanged(address indexed oldAdmin, address indexed newAdmin);
/**
* @notice Emitted when operator is added
* @param operator Operator address
*/
event OperatorAdded(address indexed operator);
/**
* @notice Emitted when operator is removed
* @param operator Operator address
*/
event OperatorRemoved(address indexed operator);
/// @notice Thrown when caller is not authorized
error Unauthorized();
/**
* @notice Set admin address
* @param newAdmin New admin address
*/
function setAdmin(address newAdmin) external;
/**
* @notice Add operator
* @param operator Operator address
*/
function addOperator(address operator) external;
/**
* @notice Remove operator
* @param operator Operator address
*/
function removeOperator(address operator) external;
/**
* @notice Get the admin address
* @return admin The admin address
*/
function getAdmin() external view returns (address admin);
/**
* @notice Check if the address is an operator
* @param operator Operator address
* @return isOpr True if the address is an operator
*/
function isOperator(address operator) external view returns (bool isOpr);
}// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.20;
/**
* @title EIP3009Args
* @author MetaLend
* @notice This struct is used to pass arguments for EIP-3009 authorization.
*/
struct EIP3009Args {
/// @notice The amount of tokens to transfer.
uint256 value;
/// @notice After which timestamp the authorization is valid.
uint256 validAfter;
/// @notice Before which timestamp the authorization is valid.
uint256 validBefore;
/// @notice Unique nonce for the authorization.
bytes32 nonce;
/// @notice The signature of the authorization.
bytes signature;
}// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.20;
import {PoolIdentifier} from "../pool/PoolIdentifier.sol";
/**
* @title RebalancingArgs
* @author MetaLend
* @notice This struct is used to pass arguments related to a pool and its pool (vault) contract.
* @dev It is used in rebalancing operations to specify the pool and pool contract.
* The `poolContract` is the address of the pool (vault) contract associated with the pool to interact with.
* This struct is typically used in conjunction with rebalancing operations to ensure that
* the correct pool and its associated contract are targeted for the operation.
*/
struct RebalancingArgs {
/// @notice The identifier of the pool
PoolIdentifier pool;
/// @notice The address of the pool (vault) contract associated with the pool.
address poolContract;
}// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.20;
import {PoolIdentifier} from "../pool/PoolIdentifier.sol";
/**
* @title RebalancingConfigArgs
* @author MetaLend
* @notice This struct is used to pass configurations related to rebalancing operations.
* @dev Use this for validating rebalancing operations by specifying approved pools, pool contracts, and destination domains.
* The `approvedPools` is an array of `PoolIdentifier` that represents the pools that are approved for the operation.
* The `approvedPoolContracts` is an array of addresses representing the pool (vault) contracts that are approved for the operation.
* The `approvedDestinationDomains` is an array of `uint32` representing the domains that are approved for the operation.
* The `fundingCap` is a `uint256` that represents maximum configured spendable cap in USDC based on aTokens (AAVE)
* in user's wallet on Linea domain (11). This is a special usecase where funds are redirected to Linea domain (11) to top up the
* wallet for spending with a MetaMask card. In this case the funds leave the rebalancer and are transferred to the owner.
* The `signature` is a bytes array that contains the signature for the approved pools,
* pool contracts, destination domains and `fundingCap` (if applicable).
* This signature is created by signing the concatenated bytes of the approved pools, pool contracts, and destination domains.
* Additionally, the `fundingCap` is included in the signature if it is greater than zero.
* This struct is typically used in conjunction with rebalancing operations to ensure that the operation
* is valid and authorized for the specified pool, pool contract, destination domain and funding cap.
* The length of `approvedPools`, `approvedPoolContracts`, and `approvedDestinationDomains` must match.
* Each entry by index in `approvedPools`, `approvedPoolContracts`, and `approvedDestinationDomains`
* corresponds to the approved pool, pool contract, and destination domain respectively.
*/
struct RebalancingConfigArgs {
/// @notice The list of approved pools for an operation
PoolIdentifier[] approvedPools;
/// @notice The list of all approved pool (vault) contracts for an operation.
address[] approvedPoolContracts;
/// @notice The list of all approved destination domains for an operation.
/// @dev Even for same domain operations, this must be defined to ensure the signature is valid.
uint32[] approvedDestinationDomains;
/// @notice The maximum configured spendable cap in USDC based on aTokens (AAVE) in user's wallet on Linea domain (11).
uint256 fundingCap;
/// @notice The signature for the approved pools, pool contracts, and destination domains.
/// @dev Created by signing the concatenated bytes of the approved pools, pool contracts, and destination domains.
bytes signature;
}// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.20;
import {EIP3009Args} from "../manager/args/EIP3009Args.sol";
import {RebalancingArgs} from "../manager/args/RebalancingArgs.sol";
import {RebalancingConfigArgs} from "../manager/args/RebalancingConfigArgs.sol";
import {Operation} from "../manager/operation/Operation.sol";
import {PoolIdentifier} from "../manager/pool/PoolIdentifier.sol";
import {IRebalancingManagerCore} from "./IRebalancingManagerCore.sol";
/**
* @title IRebalancingManager
* @author MetaLend
* @notice This interface defines the functions for the RebalancingManager contract.
*/
interface IRebalancingManager is IRebalancingManagerCore {
/**
* @notice Emitted when a deposit with authorization is made to the pool.
* @param token The address of the token being deposited.
* @param onBehalfOf The address on behalf of which the deposit is made.
* @param amount The amount of tokens deposited.
*/
event DepositWithAuthorization(address indexed token, address indexed onBehalfOf, uint256 indexed amount);
/**
* @notice Emitted when a deposit is made to the pool.
* @param token The address of the token being deposited.
* @param onBehalfOf The address on behalf of which the deposit is made.
* @param amount The amount of tokens deposited.
*/
event Deposit(address indexed token, address indexed onBehalfOf, uint256 indexed amount);
/**
* @notice Emitted when a rebalance is initiated from the source domain.
* @param onBehalfOf The address on behalf of which the rebalance is made.
* @param destinationDomain The destination domain for the rebalance.
* @param pool The pool identifier for the rebalance.
* @param token The address of the token being rebalanced.
*/
event RebalanceSrc(address indexed onBehalfOf, uint32 indexed destinationDomain, PoolIdentifier indexed pool, address token);
/**
* @notice Emitted when a rebalance is initiated to the destination domain.
* @param onBehalfOf The address on behalf of which the rebalance is made.
* @param pool The pool identifier for the rebalance.
* @param token The address of the token being rebalanced.
*/
event RebalanceDst(address indexed onBehalfOf, PoolIdentifier indexed pool, address token);
/**
* @notice Emitted when Rebalancer contract is deployed.
* @param owner The address of the owner of the Rebalancer contract.
* @param rebalancer The address of the deployed Rebalancer contract.
*/
event RebalancerDeployed(address indexed owner, address indexed rebalancer);
/**
* @notice Emitted when pool contract is updated to a specific pool and token.
* @param destinationDomain The domain supporting the pool contract
* @param poolContract The address of the pool contract being updated.
* @param pool The pool identifier for which the contract is being updated.
* @param token The address of the token associated with the pool.
* @param supported Boolean indicating whether the pool contract is supported or not.
*/
event PoolContractUpdated(uint32 destinationDomain, address indexed poolContract, PoolIdentifier pool, address token, bool supported);
/**
* @notice Emitted when a rebalancer's last rebalance timestamp is updated.
* @param rebalancer The address of the rebalancer.
* @param token The address of the token for which the timestamp is updated.
* @param lastRebalanceTimestamp The last rebalance timestamp.
*/
event RebalancerLastRebalanceTimestampUpdated(
address indexed rebalancer,
address indexed token,
uint256 indexed lastRebalanceTimestamp
);
/**
* @notice Emitted when the implementation address of the rebalancer contract is updated.
* @param rebalancerImplementation The address of the new rebalancer implementation contract.
*/
event RebalancerImplementationUpdated(address indexed rebalancerImplementation);
/**
* @notice Emitted when the rebalance fee percent is updated.
* @param rebalanceFeePercent The new rebalance fee percent.
*/
event RebalanceFeePercentUpdated(uint256 indexed rebalanceFeePercent);
/// @notice Thrown when Rebalancer contract is not found.
error RebalancerNotFound(address owner);
/// @notice Thrown when pool contract is not supported.
error PoolContractNotSupported(address poolContract);
/// @notice Thrown when the destination domain is not supported.
error DestinationDomainNotSupported(uint32 destinationDomain);
/// @notice Thrown when the signature is invalid.
error InvalidSignature(address signer, bytes signature);
/// @notice Thrown when the deadline for the transaction has passed.
error InvalidDeadline(uint256 deadline);
/// @notice Thrown when signature is used already.
error SignatureUsedAlready(bytes signature);
/// @notice Thrown when the cooldown period for rebalancing has not passed.
error RebalanceCooldownNotPassed(uint256 lastRebalanceTimestamp, uint256 rebalanceCooldown);
/// @notice Thrown when the input length is invalid.
error InvalidInputLength();
/// @notice Thrown when the operation is not allowed.
error InvalidOperation();
/**
* @notice Initializes the RebalancingManager contract.
* @param admin_ The address of the admin.
* @param rebalancerImplementation_ The address of the rebalancer implementation contract.
* @param tokenMessenger_ The address of the token messenger contract.
* @param messageTransmitter_ The address of the message transmitter contract.
* @param usdcToken_ The address of the USDC token contract.
* @param thisDestinationDomain_ The destination domain for this contract.
* @param universalSigValidator_ The address of the universal signature validator contract.
*/
function initialize(
address admin_,
address rebalancerImplementation_,
address tokenMessenger_,
address messageTransmitter_,
address usdcToken_,
uint32 thisDestinationDomain_,
address universalSigValidator_
) external;
/**
* @notice Gets or deploys a Rebalancer contract for the specified owner. Then calls the rebalancer to deposit tokens to a yield.
* When destination domain is other than this domain, the deposit is made directly to bridge for rebalance.
* @param token The address of the token being deposited. Currently only USDC is supported.
* @param onBehalfOf The address on behalf of which the deposit is made. Also the payer.
* @param destinationDomain The destination domain for the deposit.
* @param tranferArgs The transfer arguments for the EIP-3009 transfer. See `EIP3009Args` struct for details.
* @param rebalancingArgs The rebalancing arguments for the deposit. See `RebalancingArgs` struct for details.
* @param rebalancingConfigArgs The rebalancing configuration arguments. See `RebalancingConfigArgs` struct for details.
*/
function depositWithAuthorization(
address token,
address onBehalfOf,
uint32 destinationDomain,
EIP3009Args calldata tranferArgs,
RebalancingArgs calldata rebalancingArgs,
RebalancingConfigArgs calldata rebalancingConfigArgs
) external;
/**
* @notice Gets or deploys a Rebalancer contract for the specified owner. Then calls the rebalancer to deposit tokens to a yield.
* When destination domain is other than this domain, the deposit is made directly to bridge for rebalance.
* @dev The rebalancer contract must be pre-approved to spend the tokens on behalf of the owner.
* This is used for deposits with contract wallets. USDC token does not support EIP-6492 signature validation.
* That means contract wallets that are not deployed yet cannot have their signatures validated by USDC token.
* @param token The address of the token being deposited. Currently only USDC is supported.
* @param onBehalfOf The address on behalf of which the deposit is made. Also the payer.
* @param destinationDomain The destination domain for the deposit.
* @param rebalancingArgs The rebalancing arguments for the deposit. See `RebalancingArgs` struct for details.
* @param rebalancingConfigArgs The rebalancing configuration arguments. See `RebalancingConfigArgs` struct for details.
*/
function deposit(
address token,
address onBehalfOf,
uint32 destinationDomain,
RebalancingArgs calldata rebalancingArgs,
RebalancingConfigArgs calldata rebalancingConfigArgs
) external;
/**
* @notice Gets a Rebalancer contract for the specified owner. Then calls the rebalancer to rebalance tokens from the source domain.
* @dev In this case the Rebalancer must already exist.
* @param onBehalfOf The address on behalf of which the rebalance is made.
* @param destinationDomain The destination domain for the rebalance.
* @param amount The amount of tokens being rebalanced. Use MAX_UINT256 for full amount.
* @param rebalancingArgsFrom The rebalancing arguments for the rebalance. See `RebalancingArgs` struct for details.
* @param rebalancingArgsTo The rebalancing arguments for the destination. See `RebalancingArgs` struct for details.
* @param rebalancingConfigArgs The rebalancing configuration arguments. See `RebalancingConfigArgs` struct for details.
*/
function rebalanceUsdcSrc(
address onBehalfOf,
uint32 destinationDomain,
uint256 amount,
RebalancingArgs calldata rebalancingArgsFrom,
RebalancingArgs calldata rebalancingArgsTo,
RebalancingConfigArgs calldata rebalancingConfigArgs
) external;
/**
* @notice Gets or deploys a Rebalancer contract for the specified owner. Then calls the rebalancer to rebalance on destination domain.
* @param onBehalfOf The address on behalf of which the rebalance is made.
* @param rebalancingArgs The rebalancing arguments for the deposit. See `RebalancingArgs` struct for details.
* @param rebalancingConfigArgs The rebalancing configuration arguments. See `RebalancingConfigArgs` struct for details.
* @param message The message emitted by MessageSent event during depositForBurn.
* @param attestation The attestation retrieved from the CCTP API.
*/
function rebalanceUsdcDst(
address onBehalfOf,
RebalancingArgs calldata rebalancingArgs,
RebalancingConfigArgs calldata rebalancingConfigArgs,
bytes calldata message,
bytes calldata attestation
) external;
/**
* @notice Gets a Rebalancer contract for the specified owner. Then calls the rebalancer to rebalance token on this domain.
* @dev In this case the Rebalancer must already exist.
* @param onBehalfOf The address on behalf of which the rebalance is made.
* @param rebalancingArgsFrom The rebalancing arguments for the source pool. See `RebalancingArgs` struct for details.
* @param rebalancingArgsTo The rebalancing arguments for the destination pool. See `RebalancingArgs` struct for details.
* @param rebalancingConfigArgs The rebalancing configuration arguments. See `RebalancingConfigArgs` struct for details.
*/
function rebalanceThisDomain(
address onBehalfOf,
address token,
RebalancingArgs calldata rebalancingArgsFrom,
RebalancingArgs calldata rebalancingArgsTo,
RebalancingConfigArgs calldata rebalancingConfigArgs
) external;
/**
* @notice Gets a Rebalancer contract for the specified owner. Then calls the rebalancer to fund the owner tokens.
* @dev In this case the Rebalancer must already exist. Can be called only on Linea domain (11).
* @param onBehalfOf The address on behalf of which the funding is made.
* @param rebalancingConfigArgs The rebalancing configuration arguments. See `RebalancingConfigArgs` struct for details.
*/
function fundOwner(address onBehalfOf, RebalancingConfigArgs calldata rebalancingConfigArgs) external;
/**
* @notice Gets a Rebalancer contract for the specified owner. Then calls the rebalancer to withdraw tokens from specified pool.
* @dev In this case the Rebalancer must already exist. Callable by either the owner or by operator on their behalf.
* @param onBehalfOf The address on behalf of which the withdrawal is made.
* @param token The address of the token being withdrawn.
* @param pool The pool identifier from which the tokens are withdrawn.
* @param poolContract The address of the pool contract from which the tokens are withdrawn.
* @param amount The amount of tokens withdrawn.
* @param deadline The timestamp before which the withdrawal is valid. Needed only when done by the operator.
* @param signature The signature for the authorization. Necessary only when done by the operator.
*/
function withdrawFromPool(
address onBehalfOf,
address token,
PoolIdentifier pool,
address poolContract,
uint256 amount,
uint256 deadline,
bytes calldata signature
) external;
/**
* @notice Claims rewards from Morpho vaults.
* @dev Callable by only the owner.
* @param rewardDistributor The address of the reward distributor contract.
* @param rewardToken The address of the reward token contract.
* @param claimable The overall claimable amount of token rewards.
* @param proof The Merkle proof array that verifies the claim's validity.
*/
function claimRewardsMorpho(address rewardDistributor, address rewardToken, uint256 claimable, bytes32[] calldata proof) external;
/**
* @notice Claims rewards. Rewards are from Morpho, Merkl, and any other similar reward distributor.
* @dev Callable by only the owner.
* @param rewardDistributor The address of the reward distributor contracts.
* @param rewardTokens The addresses of the reward token contracts.
* @param claimable The overall claimable amounts of token rewards.
* @param proofs The Merkle proof arrays that verify the claims' validity.
*/
function claimRewards(
address rewardDistributor,
address[] calldata rewardTokens,
uint256[] calldata claimable,
bytes32[][] calldata proofs
) external;
/**
* @notice Claims rewards from Aave lending pool.
* @dev Callable by only the owner.
* @param rewardsController The address of the Aave rewards controller contract.
* @param assets The list of asset addresses to claim rewards for.
*/
function claimRewardsAave(address rewardsController, address[] calldata assets) external;
/**
* @notice Updates the pool contract addresses to specified pools and associated tokens.
* @dev This function is only callable by the admin.
* The length of `poolContracts`, `pools`, `tokens` and `supported` must be the same.
* @param destinationDomain The destination domain for which the pool contracts are being updated.
* @param poolContracts The addresses of the pool contracts to be updated.
* @param pools The pool identifiers corresponding to the pool contracts.
* @param tokens The addresses of the tokens for the pools.
* @param supported The boolean array indicating whether the pool contract is supported or not.
*/
function updatePoolContract(
uint32 destinationDomain,
address[] calldata poolContracts,
PoolIdentifier[] calldata pools,
address[] calldata tokens,
bool[] calldata supported
) external;
/**
* @notice Updates the implementation address of the rebalancer contract.
* @dev This function is only callable by the admin.
* @param rebalancerImplementation The address of the new rebalancer implementation contract.
*/
function updateRebalancerImplementation(address rebalancerImplementation) external;
/**
* @notice Updates the fees for operations on specified domains. Effectively supporting the domain.
* @dev This function is only callable by the admin.
* The value must be a fractional human-readable fee with 3 decimal precision (e.g. `1` if fee = 0.001 tokens with denominator = 1000).
* The length of `domains`, `operations` and `fees` must be the same.
* @param domains The destination domains for which the fees are being updated.
* @param operations The operations for which the fees are being updated.
* @param fees The new fees for the specified domains and operations
* > 0 if the operation on destination domain is supported, 0 if not.
*/
function setDomainFeeForOperation(uint32[] calldata domains, Operation[] calldata operations, uint256[] calldata fees) external;
/**
* @notice Updates the rebalance fee percent.
* @dev This function is only callable by the admin.
* @param rebalanceFeePercent The new rebalance fee percent.
*/
function setRebalanceFeePercent(uint256 rebalanceFeePercent) external;
/**
* @notice Sets the rebalance cooldown period.
* @dev This function is only callable by the admin.
* @param cooldown The new cooldown period for rebalancing in seconds.
*/
function setRebalanceCooldown(uint256 cooldown) external;
/**
* @notice Gets the amount of rebalance fee for a given amount.
* @dev Ignored on Linea domain.
* @param amount The amount of tokens for which the rebalance fee is being calculated.
* @return rebalanceFee The calculated rebalance fee for the specified amount.
*/
function getRebalanceFee(uint256 amount) external view returns (uint256 rebalanceFee);
/**
* @notice Retrieves the transaction fee for operation on a specific destination domain.
* @param destinationDomain The destination domain for which the gas transaction fee is being queried.
* @param token The address of the token for which the gas transaction fee is being queried.
* @param operation The operation for which the gas transaction fee is being queried.
* @param useThisDomain Boolean indicating whether to use this domain's fee for same domain operations.
* In this case the destinationDomain is ignored.
*/
function getDestinationDomainGasTransactionFeeForOperation(
uint32 destinationDomain,
address token,
Operation operation,
bool useThisDomain
) external view returns (uint256 gasTransactionFee);
/**
* @notice Retrieves the token messenger contract address.
* @return tokenMessenger The address of the token messenger contract.
*/
function getUsdcCctpTokenMessenger() external view returns (address tokenMessenger);
/**
* @notice Retrieves the message transmitter contract address.
* @return messageTransmitter The address of the message transmitter contract.
*/
function getUsdcCctpMessageTransmitter() external view returns (address messageTransmitter);
/**
* @notice Retrieves the USDC token contract address.
* @return usdcToken The address of the USDC token contract.
*/
function getUsdcToken() external view returns (address usdcToken);
/**
* @notice Retrieves the pool and token for given pool contract.
* @param destinationDomain The destination domain for which the pool contracts is queried.
* @param poolContract The address of the pool contract for which the pool and token are queried.
* @param pool The pool identifier for which the contract is being queried.
* @param token The address of the token associated with the pool contract being queried.
* @return supported True if the pool contract is supported, false otherwise.
*/
function getPoolContractSupported(
uint32 destinationDomain,
address poolContract,
PoolIdentifier pool,
address token
) external view returns (bool supported);
/**
* @notice Checks if the current domain is Linea.
* @return isLinea True if the current domain is Linea, false otherwise.
*/
function isLineaDomain() external view returns (bool isLinea);
/**
* @notice Retrieves the Linea AAVE USDC aToken contract address.
* @return aToken The address of the Linea AAVE USDC aToken contract.
*/
function getLineaUsdcAaveToken() external pure returns (address aToken);
}// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.20;
/**
* @title IRebalancingManagerCore
* @author MetaLend
* @notice Core rebalancing manager interface to read rebalancer implementation
*/
interface IRebalancingManagerCore {
/**
* @notice Returns the address of the rebalancer implementation contract
* @dev Use this with _implementation() in the proxy contract to get the implementation address
* @return rebalancerImplementation The address of the rebalancer implementation contract
*/
function getRebalancerImplementation() external view returns (address rebalancerImplementation);
}// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.20;
/**
* @title Operation
* @author MetaLend
* @notice Enum representing operations.
* @dev This enum is used to identify different supported operations in the system.
*/
enum Operation {
DEPOSIT,
WITHDRAW,
REBALANCE_THIS_DOMAIN,
REBALANCE_SRC_DOMAIN,
REBALANCE_DST_DOMAIN,
FUND_OWNER
}// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.20;
/**
* @title PoolIdentifier
* @author MetaLend
* @notice Enum representing different pool identifiers.
* @dev This enum is used to identify different supported pools in the system.
*/
enum PoolIdentifier {
AAVE_POOL,
MORPHO_POOL,
EULER_POOL
}// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.20;
import {PoolIdentifier} from "./PoolIdentifier.sol";
/**
* @title PoolToken
* @author MetaLend
* @notice Struct representing a token in a specific pool.
* @dev This struct is used to associate a token with its corresponding pool identifier.
* Used while mapping pool contracts to their tokens.
*/
struct PoolToken {
/// @notice The identifier of the pool this token belongs to.
PoolIdentifier pool;
/// @notice The address of the underlying token of this pool.
address token;
}// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.20;
import {PoolIdentifier} from "../manager/pool/PoolIdentifier.sol";
/**
* @title IRebalancer
* @author MetaLend
* @notice This interface defines the functions for the Rebalancer contract.
*/
interface IRebalancer {
/**
* @notice Emitted when the Rebalancer contract pays the rebalance fee.
* @param token The address of the token used to pay the rebalance fee.
* @param owner The address of the owner who pays the rebalance fee.
* @param amount The amount of tokens paid as the rebalance fee.
*/
event PayRebalanceFee(address indexed token, address indexed owner, uint256 amount);
/**
* @notice Emitted when the Rebalancer contract funds the owner with tokens.
* @param owner The address of the owner who receives the tokens.
* @param token The address of the token transferred to the owner.
* @param amount The amount of tokens transferred to the owner.
*/
event FundOwner(address indexed owner, address indexed token, uint256 amount);
/**
* @notice Emitted when deposit to the pool fails and the Rebalancer contract withdraws tokens to the owner.
* @param token The address of the token that failed to deposit.
* @param amount The amount of tokens that failed to deposit.
*/
event OnDepositFailureWithdraw(address indexed token, uint256 amount);
/// @notice Thrown when caller is not authorized
error Unauthorized();
/// @notice Thrown if there is not enough tokens to cover the gas transaction fee
error InsufficientAmountForGasTransactionFee();
/// @notice Thrown if there is not enough allowance for transferring tokens
error ERC20InsufficientAllowance(address token);
/// @notice Thrown if the funding cannot be completed
error FundingUnavailable();
/**
* @notice Approves the pool and deposits the specified amount of tokens.
* @dev This function is called by the RebalancingManager contract.
* @param destinationDomain The domain to which the tokens will be bridged. For same domain, use MAX_UINT32.
* @param pool The identifier of the pool to deposit into.
* @param poolContract The address of the pool contract to approve and deposit into.
* @param token The address of the token to deposit.
* @param amount The amount of tokens to deposit.
* @param validAfter The timestamp after which the approval is valid.
* @param validBefore The timestamp before which the approval is valid.
* @param nonce The nonce for the approval.
* @param v v of approval signature
* @param r r of approval signature
* @param s s of approval signature
*/
function approveAndDeposit(
uint32 destinationDomain,
PoolIdentifier pool,
address poolContract,
address token,
uint256 amount,
uint256 validAfter,
uint256 validBefore,
bytes32 nonce,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @notice Approves the pool and deposits the specified amount of tokens.
* @dev This function is called by the RebalancingManager contract.
* This rebalancer must be pre-approved to spend the tokens.
* @param destinationDomain The domain to which the tokens will be bridged. For same domain, use MAX_UINT32.
* @param pool The identifier of the pool to deposit into.
* @param poolContract The address of the pool contract to approve and deposit into.
* @param token The address of the token to deposit.
* @return amount The amount of tokens deposited.
*/
function approveAndDeposit(
uint32 destinationDomain,
PoolIdentifier pool,
address poolContract,
address token
) external returns (uint256 amount);
/**
* @notice Receives USDC tokens from bridge, approves the pool contract, and deposits the tokens.
* @dev This function is called by the RebalancingManager contract.
* @param pool The identifier of the pool to deposit into.
* @param poolContract The address of the pool contract to approve and deposit into.
* @param message The message emitted by MessageSent event during depositForBurn.
* @param attestation The attestation retrieved from the CCTP API.
*/
function receiveUsdcApproveAndDeposit(
PoolIdentifier pool,
address poolContract,
bytes calldata message,
bytes calldata attestation
) external;
/**
* @notice Withdraws USDC from pool and deposits it for burning on the specified destination domain.
* @dev This function is called by the RebalancingManager contract.
* @param amount The amount of USDC to withdraw and deposit for burning.
* @param pool The identifier of the pool to withdraw from.
* @param poolContract The address of the pool contract to withdraw from.
* @param destinationDomain The domain to which the USDC will be bridged.
*/
function withdrawAndDepositForBurnUsdc(uint256 amount, PoolIdentifier pool, address poolContract, uint32 destinationDomain) external;
/**
* @notice Withdraws tokens from the source pool and redeposits them into the destination pool on the same domain.
* @dev This function is called by the RebalancingManager contract.
* @param token The address of the token to withdraw and deposit.
* @param srcPool The identifier of the source pool to withdraw from.
* @param srcPoolContract The address of the source pool contract to withdraw from.
* @param dstPool The identifier of the destination pool to deposit into.
* @param dstPoolContract The address of the destination pool contract to deposit into.
*/
function withdrawAndRedeposit(
address token,
PoolIdentifier srcPool,
address srcPoolContract,
PoolIdentifier dstPool,
address dstPoolContract
) external;
/**
* @notice Funds the owner with aTokens (AAVE).
* @dev This function is called by the RebalancingManager contract.
* @param fundingCap The maximum configured spendable cap in USDC based on aTokens (AAVE) in user's wallet on Linea domain (11).
*/
function fundOwnerWithUsdcAaveToken(uint256 fundingCap) external;
/**
* @notice Withdraws the specified amount of tokens from pool and transfers them to the owner.
* @dev This function is called by the RebalancingManager contract.
* If amount is MAX_UINT256, it withdraws all available tokens.
* @param token The address of the token to withdraw.
* @param pool The identifier of the pool to withdraw from.
* @param poolContract The address of the pool contract to withdraw from.
* @param amount The amount of tokens to withdraw.
* @param payFee Whether to pay the gas transaction fee. This depends if the tx is called on behalf of the owner or not.
*/
function withdrawFromPoolToOwner(address token, PoolIdentifier pool, address poolContract, uint256 amount, bool payFee) external;
/**
* @notice Claims rewards from Morpho vaults.
* @dev This function is called by the RebalancingManager contract.
* @param rewardDistributor The address of the reward distributor contract.
* @param rewardToken The address of the reward token contract.
* @param claimable The overall claimable amount of token rewards.
* @param proof The Merkle proof array that verifies the claim's validity.
*/
function claimRewardsMorpho(address rewardDistributor, address rewardToken, uint256 claimable, bytes32[] calldata proof) external;
/**
* @notice Claims rewards. Rewards are from Morpho, Merkl, and any other similar reward distributor.
* @dev This function is called by the RebalancingManager contract. The rewardToken array must be unique addresses.
* @param rewardDistributor The address of the reward distributor contract.
* @param rewardToken The addresses of the reward token contracts.
* @param claimable The overall claimable amounts of token rewards.
* @param proof The Merkle proof arrays that verify the claims' validity.
*/
function claimRewards(
address rewardDistributor,
address[] calldata rewardToken,
uint256[] calldata claimable,
bytes32[][] calldata proof
) external;
/**
* @notice Claims rewards from Aave lending pool.
* @dev This function is called by the RebalancingManager contract.
* @param rewardsController The address of the Aave rewards controller contract.
* @param assets The list of asset addresses to claim rewards for.
*/
function claimRewardsAave(address rewardsController, address[] calldata assets) external;
/**
* @notice Returns the address of the owner of the Rebalancer contract.
* @return owner The address of the owner.
*/
function getOwner() external view returns (address owner);
}// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.20;
/**
* @title RebalancerCore
* @author MetaLend
* @notice Abstract contract that provides default storage layout for proxy and implementation contract
*/
abstract contract RebalancerCore {
/// @notice Manager contract providing rebalancing functionality and access control and implementation
address internal _rebalancingManager;
/// @notice Address of the owner of this rebalancer
address internal _owner;
/**
* @notice Emitted when the rebalancer is initialized
* @param rebalancer Address of this rebalancer
* @param manager Address of the rebalancing manager
* @param owner Address of the owner of this rebalancer
*/
event RebalancerInitialized(address indexed rebalancer, address indexed manager, address indexed owner);
}// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.20;
import {Proxy} from "@openzeppelin/contracts/proxy/Proxy.sol";
import {IRebalancingManagerCore} from "../manager/IRebalancingManagerCore.sol";
import {RebalancerCore} from "./RebalancerCore.sol";
/**
* @title RebalancerProxy
* @author MetaLend
* @notice Proxy contract for the rebalancer
* @dev This contract is created to be used as a proxy for the rebalancer implementation managed by the rebalancing manager
*/
contract RebalancerProxy is RebalancerCore, Proxy {
/**
* @notice Constructor for the RebalancerProxy contract
* @param manager_ The address of the rebalancing manager
* @param owner_ The address of the owner of the rebalancer
*/
constructor(address manager_, address owner_) {
_rebalancingManager = manager_;
_owner = owner_;
emit RebalancerInitialized(address(this), _rebalancingManager, _owner);
}
/**
* @notice Returns the address of the implementation contract
* @dev This is used by the proxy `_fallback` function to delegate calls to the implementation contract
*/
function _implementation() internal view override returns (address implementation) {
return IRebalancingManagerCore(_rebalancingManager).getRebalancerImplementation();
}
}{
"metadata": {
"bytecodeHash": "none",
"useLiteralContent": true
},
"optimizer": {
"enabled": true,
"runs": 999999
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "paris",
"libraries": {
"contracts/manager/RebalancingManager.sol": {
"FeesLib": "0xdceac80c8429140450b45697aae1ccb8dc921ccb"
}
}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[],"name":"Create2EmptyBytecode","type":"error"},{"inputs":[],"name":"Create2FailedDeployment","type":"error"},{"inputs":[{"internalType":"uint256","name":"balance","type":"uint256"},{"internalType":"uint256","name":"needed","type":"uint256"}],"name":"Create2InsufficientBalance","type":"error"},{"inputs":[{"internalType":"uint32","name":"destinationDomain","type":"uint32"}],"name":"DestinationDomainNotSupported","type":"error"},{"inputs":[{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"InvalidDeadline","type":"error"},{"inputs":[],"name":"InvalidInputLength","type":"error"},{"inputs":[],"name":"InvalidOperation","type":"error"},{"inputs":[{"internalType":"address","name":"signer","type":"address"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"InvalidSignature","type":"error"},{"inputs":[{"internalType":"address","name":"poolContract","type":"address"}],"name":"PoolContractNotSupported","type":"error"},{"inputs":[{"internalType":"uint256","name":"lastRebalanceTimestamp","type":"uint256"},{"internalType":"uint256","name":"rebalanceCooldown","type":"uint256"}],"name":"RebalanceCooldownNotPassed","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"RebalancerNotFound","type":"error"},{"inputs":[{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"SignatureUsedAlready","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldAdmin","type":"address"},{"indexed":true,"internalType":"address","name":"newAdmin","type":"address"}],"name":"AdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"address","name":"onBehalfOf","type":"address"},{"indexed":true,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"address","name":"onBehalfOf","type":"address"},{"indexed":true,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"DepositWithAuthorization","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"}],"name":"OperatorAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"}],"name":"OperatorRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint32","name":"destinationDomain","type":"uint32"},{"indexed":true,"internalType":"address","name":"poolContract","type":"address"},{"indexed":false,"internalType":"enum PoolIdentifier","name":"pool","type":"uint8"},{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"bool","name":"supported","type":"bool"}],"name":"PoolContractUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"onBehalfOf","type":"address"},{"indexed":true,"internalType":"enum PoolIdentifier","name":"pool","type":"uint8"},{"indexed":false,"internalType":"address","name":"token","type":"address"}],"name":"RebalanceDst","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"rebalanceFeePercent","type":"uint256"}],"name":"RebalanceFeePercentUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"onBehalfOf","type":"address"},{"indexed":true,"internalType":"uint32","name":"destinationDomain","type":"uint32"},{"indexed":true,"internalType":"enum PoolIdentifier","name":"pool","type":"uint8"},{"indexed":false,"internalType":"address","name":"token","type":"address"}],"name":"RebalanceSrc","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"rebalancer","type":"address"}],"name":"RebalancerDeployed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"rebalancerImplementation","type":"address"}],"name":"RebalancerImplementationUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"rebalancer","type":"address"},{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"uint256","name":"lastRebalanceTimestamp","type":"uint256"}],"name":"RebalancerLastRebalanceTimestampUpdated","type":"event"},{"inputs":[],"name":"DOMAIN_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FEE_DENOMINATOR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"LINEA_DOMAIN_ID","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_REBALANCING_FEE_PERCENT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"NAME_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"VERSION_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"WITHDRAWAL_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"}],"name":"addOperator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"rewardDistributor","type":"address"},{"internalType":"address[]","name":"rewardTokens","type":"address[]"},{"internalType":"uint256[]","name":"claimable","type":"uint256[]"},{"internalType":"bytes32[][]","name":"proofs","type":"bytes32[][]"}],"name":"claimRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"rewardsController","type":"address"},{"internalType":"address[]","name":"assets","type":"address[]"}],"name":"claimRewardsAave","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"rewardDistributor","type":"address"},{"internalType":"address","name":"rewardToken","type":"address"},{"internalType":"uint256","name":"claimable","type":"uint256"},{"internalType":"bytes32[]","name":"proof","type":"bytes32[]"}],"name":"claimRewardsMorpho","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"onBehalfOf","type":"address"},{"internalType":"uint32","name":"destinationDomain","type":"uint32"},{"components":[{"internalType":"enum PoolIdentifier","name":"pool","type":"uint8"},{"internalType":"address","name":"poolContract","type":"address"}],"internalType":"struct RebalancingArgs","name":"rebalancingArgs","type":"tuple"},{"components":[{"internalType":"enum PoolIdentifier[]","name":"approvedPools","type":"uint8[]"},{"internalType":"address[]","name":"approvedPoolContracts","type":"address[]"},{"internalType":"uint32[]","name":"approvedDestinationDomains","type":"uint32[]"},{"internalType":"uint256","name":"fundingCap","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct RebalancingConfigArgs","name":"rebalancingConfigArgs","type":"tuple"}],"name":"deposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"onBehalfOf","type":"address"},{"internalType":"uint32","name":"destinationDomain","type":"uint32"},{"components":[{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"validAfter","type":"uint256"},{"internalType":"uint256","name":"validBefore","type":"uint256"},{"internalType":"bytes32","name":"nonce","type":"bytes32"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct EIP3009Args","name":"tranferArgs","type":"tuple"},{"components":[{"internalType":"enum PoolIdentifier","name":"pool","type":"uint8"},{"internalType":"address","name":"poolContract","type":"address"}],"internalType":"struct RebalancingArgs","name":"rebalancingArgs","type":"tuple"},{"components":[{"internalType":"enum PoolIdentifier[]","name":"approvedPools","type":"uint8[]"},{"internalType":"address[]","name":"approvedPoolContracts","type":"address[]"},{"internalType":"uint32[]","name":"approvedDestinationDomains","type":"uint32[]"},{"internalType":"uint256","name":"fundingCap","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct RebalancingConfigArgs","name":"rebalancingConfigArgs","type":"tuple"}],"name":"depositWithAuthorization","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"onBehalfOf","type":"address"},{"components":[{"internalType":"enum PoolIdentifier[]","name":"approvedPools","type":"uint8[]"},{"internalType":"address[]","name":"approvedPoolContracts","type":"address[]"},{"internalType":"uint32[]","name":"approvedDestinationDomains","type":"uint32[]"},{"internalType":"uint256","name":"fundingCap","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct RebalancingConfigArgs","name":"rebalancingConfigArgs","type":"tuple"}],"name":"fundOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getAdmin","outputs":[{"internalType":"address","name":"admin","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"destinationDomain","type":"uint32"},{"internalType":"address","name":"token","type":"address"},{"internalType":"enum Operation","name":"operation","type":"uint8"},{"internalType":"bool","name":"useThisDomain","type":"bool"}],"name":"getDestinationDomainGasTransactionFeeForOperation","outputs":[{"internalType":"uint256","name":"gasTransactionFee","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLineaUsdcAaveToken","outputs":[{"internalType":"address","name":"aToken","type":"address"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint32","name":"destinationDomain","type":"uint32"},{"internalType":"address","name":"poolContract","type":"address"},{"internalType":"enum PoolIdentifier","name":"pool","type":"uint8"},{"internalType":"address","name":"token","type":"address"}],"name":"getPoolContractSupported","outputs":[{"internalType":"bool","name":"supported","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"getRebalanceFee","outputs":[{"internalType":"uint256","name":"rebalanceFee","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRebalancerImplementation","outputs":[{"internalType":"address","name":"rebalancerImplementation","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getUsdcCctpMessageTransmitter","outputs":[{"internalType":"address","name":"messageTransmitter","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getUsdcCctpTokenMessenger","outputs":[{"internalType":"address","name":"tokenMessenger","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getUsdcToken","outputs":[{"internalType":"address","name":"usdcToken","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"admin_","type":"address"},{"internalType":"address","name":"rebalancerImplementation_","type":"address"},{"internalType":"address","name":"tokenMessenger_","type":"address"},{"internalType":"address","name":"messageTransmitter_","type":"address"},{"internalType":"address","name":"usdcToken_","type":"address"},{"internalType":"uint32","name":"thisDestinationDomain_","type":"uint32"},{"internalType":"address","name":"universalSigValidator_","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"isLineaDomain","outputs":[{"internalType":"bool","name":"isLinea","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"}],"name":"isOperator","outputs":[{"internalType":"bool","name":"isOpr","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rebalanceCooldown","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"onBehalfOf","type":"address"},{"internalType":"address","name":"token","type":"address"},{"components":[{"internalType":"enum PoolIdentifier","name":"pool","type":"uint8"},{"internalType":"address","name":"poolContract","type":"address"}],"internalType":"struct RebalancingArgs","name":"rebalancingArgsFrom","type":"tuple"},{"components":[{"internalType":"enum PoolIdentifier","name":"pool","type":"uint8"},{"internalType":"address","name":"poolContract","type":"address"}],"internalType":"struct RebalancingArgs","name":"rebalancingArgsTo","type":"tuple"},{"components":[{"internalType":"enum PoolIdentifier[]","name":"approvedPools","type":"uint8[]"},{"internalType":"address[]","name":"approvedPoolContracts","type":"address[]"},{"internalType":"uint32[]","name":"approvedDestinationDomains","type":"uint32[]"},{"internalType":"uint256","name":"fundingCap","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct RebalancingConfigArgs","name":"rebalancingConfigArgs","type":"tuple"}],"name":"rebalanceThisDomain","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"onBehalfOf","type":"address"},{"components":[{"internalType":"enum PoolIdentifier","name":"pool","type":"uint8"},{"internalType":"address","name":"poolContract","type":"address"}],"internalType":"struct RebalancingArgs","name":"rebalancingArgs","type":"tuple"},{"components":[{"internalType":"enum PoolIdentifier[]","name":"approvedPools","type":"uint8[]"},{"internalType":"address[]","name":"approvedPoolContracts","type":"address[]"},{"internalType":"uint32[]","name":"approvedDestinationDomains","type":"uint32[]"},{"internalType":"uint256","name":"fundingCap","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct RebalancingConfigArgs","name":"rebalancingConfigArgs","type":"tuple"},{"internalType":"bytes","name":"message","type":"bytes"},{"internalType":"bytes","name":"attestation","type":"bytes"}],"name":"rebalanceUsdcDst","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"onBehalfOf","type":"address"},{"internalType":"uint32","name":"destinationDomain","type":"uint32"},{"internalType":"uint256","name":"amount","type":"uint256"},{"components":[{"internalType":"enum PoolIdentifier","name":"pool","type":"uint8"},{"internalType":"address","name":"poolContract","type":"address"}],"internalType":"struct RebalancingArgs","name":"rebalancingArgsFrom","type":"tuple"},{"components":[{"internalType":"enum PoolIdentifier","name":"pool","type":"uint8"},{"internalType":"address","name":"poolContract","type":"address"}],"internalType":"struct RebalancingArgs","name":"rebalancingArgsTo","type":"tuple"},{"components":[{"internalType":"enum PoolIdentifier[]","name":"approvedPools","type":"uint8[]"},{"internalType":"address[]","name":"approvedPoolContracts","type":"address[]"},{"internalType":"uint32[]","name":"approvedDestinationDomains","type":"uint32[]"},{"internalType":"uint256","name":"fundingCap","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct RebalancingConfigArgs","name":"rebalancingConfigArgs","type":"tuple"}],"name":"rebalanceUsdcSrc","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"rebalancers","outputs":[{"internalType":"address","name":"rebalancer","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"}],"name":"removeOperator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"roles","outputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newAdmin","type":"address"}],"name":"setAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32[]","name":"domains","type":"uint32[]"},{"internalType":"enum Operation[]","name":"operations","type":"uint8[]"},{"internalType":"uint256[]","name":"fees","type":"uint256[]"}],"name":"setDomainFeeForOperation","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"cooldown","type":"uint256"}],"name":"setRebalanceCooldown","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"rebalancingFeePercent","type":"uint256"}],"name":"setRebalanceFeePercent","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"thisDestinationDomain","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"universalSigValidator","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"destinationDomain","type":"uint32"},{"internalType":"address[]","name":"poolContracts","type":"address[]"},{"internalType":"enum PoolIdentifier[]","name":"pools","type":"uint8[]"},{"internalType":"address[]","name":"tokens","type":"address[]"},{"internalType":"bool[]","name":"supported","type":"bool[]"}],"name":"updatePoolContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"rebalancerImplementation","type":"address"}],"name":"updateRebalancerImplementation","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"onBehalfOf","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"enum PoolIdentifier","name":"pool","type":"uint8"},{"internalType":"address","name":"poolContract","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"withdrawFromPool","outputs":[],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
608060405234801561001057600080fd5b50614c3b806100206000396000f3fe60806040523480156200001157600080fd5b5060043610620003195760003560e01c8063923ca9a411620001a5578063b3de272d11620000f5578063db1c4ded11620000a3578063f95aea4c116200007a578063f95aea4c1462000803578063fa62ebaa146200081a578063fdfd5a25146200083957600080fd5b8063db1c4ded14620007a8578063e9e833e614620007c3578063eb39e9ba14620007da57600080fd5b8063b68a659211620000d8578063b68a6592146200076b578063bd2f7172146200078a578063d73792a9146200079d57600080fd5b8063b3de272d146200074a578063b5fa9dd2146200075457600080fd5b8063a44d57a01162000153578063ab08c5241162000136578063ab08c5241462000705578063ac8a584a146200071c578063ac98d9b0146200073357600080fd5b8063a44d57a014620006ad578063a9fda0eb14620006e657600080fd5b80639358a04111620001885780639358a041146200065c5780639870d7fe146200067357806399374642146200068a57600080fd5b8063923ca9a414620005e857806392765861146200064557600080fd5b8063468f8f52116200026d5780636e9960c3116200021b5780638057739911620001f25780638057739914620005a157806381ca52cf14620005c857806382e5bec314620005df57600080fd5b80636e9960c31462000554578063704b6c0214620005735780637e22e6eb146200058a57600080fd5b806351f0caef116200025057806351f0caef14620004d85780636aa9541414620004e25780636d70f7ae14620004f957600080fd5b8063468f8f5214620004aa57806351c2569814620004c157600080fd5b806324703dbc11620002cb5780633b44390a11620002ae5780633b44390a14620003f65780633f1b7020146200043657806344f2c5aa146200044d57600080fd5b806324703dbc14620003c85780633acbc1e914620003df57600080fd5b80630c3ee3ac11620003005780630c3ee3ac146200034e5780631ffe1422146200037857806320606b7014620003a057600080fd5b80630538153d146200031e578063091373601462000337575b600080fd5b620003356200032f36600462003610565b62000861565b005b62000335620003483660046200369e565b62000b72565b620003656200035f366004620036ec565b62000c34565b6040519081526020015b60405180910390f35b6200038f620003893660046200375b565b62000d2c565b60405190151581526020016200036f565b620003657f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f81565b62000335620003d936600462003807565b62000d6c565b62000335620003f036600462003860565b62000e0e565b60065473ffffffffffffffffffffffffffffffffffffffff165b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016200036f565b6200033562000447366004620038b4565b62000f77565b60408051808201909152601481527f4d6574614c656e6420526562616c616e63696e67000000000000000000000000602090910152620003657f85d7f6d19ce33abcd38ffd3d658144a573976d214de6c9a3ee824fff292c8d3181565b62000335620004bb36600462003949565b6200103f565b62000335620004d2366004620039ac565b62001100565b620003656103e881565b62000335620004f336600462003a51565b620012fd565b6200038f6200050a36600462003949565b73ffffffffffffffffffffffffffffffffffffffff166000908152600160205260409020547f97667070c54ef182b0f5858b034beac1b6f3089aa2d3188bb1e8929f4fa9b9291490565b60005473ffffffffffffffffffffffffffffffffffffffff1662000410565b620003356200058436600462003949565b620015de565b620003356200059b36600462003b37565b6200174e565b600f54620005b29063ffffffff1681565b60405163ffffffff90911681526020016200036f565b62000335620005d93660046200369e565b620018fa565b620005b2600b81565b60408051808201909152600581527f332e302e30000000000000000000000000000000000000000000000000000000602090910152620003657fd7a1ce683065975771bedf401ecab037f4f4c62cc51fefdc8b39dd246ff0343a81565b620003356200065636600462003bdb565b62001951565b620003356200066d36600462003ca0565b62001b1e565b620003356200068436600462003949565b62001ccb565b620003656200069b36600462003949565b60016020526000908152604090205481565b62000410620006be36600462003949565b60086020526000908152604090205473ffffffffffffffffffffffffffffffffffffffff1681565b60025473ffffffffffffffffffffffffffffffffffffffff1662000410565b620003356200071636600462003d12565b62001e33565b620003356200072d36600462003949565b62002031565b620003356200074436600462003dc7565b620020d4565b6200036560075481565b62000365620007653660046200369e565b6200217c565b60045473ffffffffffffffffffffffffffffffffffffffff1662000410565b6200038f600f5463ffffffff16600b1490565b62000365620186a081565b73374d7860c4f2f604de0191298dd393703cce84f362000410565b62000335620007d436600462003e3e565b620021dc565b600f546200041090640100000000900473ffffffffffffffffffffffffffffffffffffffff1681565b620003356200081436600462003ee2565b6200228a565b60035473ffffffffffffffffffffffffffffffffffffffff1662000410565b620003657fc4c90a8c884e43804bc3e7d7a39f8a2f6bbfa0212985c48e240d628c228f046081565b336000908152600160205260409020547f97667070c54ef182b0f5858b034beac1b6f3089aa2d3188bb1e8929f4fa9b92914620008ca576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000620008d786620024d0565b9050620008eb604084016020850162003949565b73ffffffffffffffffffffffffffffffffffffffff1662000913604086016020870162003949565b73ffffffffffffffffffffffffffffffffffffffff160362000961576040517f398d4d3200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600f54620009979063ffffffff1662000981604087016020880162003949565b62000990602088018862003f6c565b886200254e565b600f54620009b0908490849063ffffffff1688620025df565b620009bc86836200261f565b620009c881866200276a565b73ffffffffffffffffffffffffffffffffffffffff811663fe4c85bc86620009f4602088018862003f6c565b62000a066040890160208a0162003949565b62000a15602089018962003f6c565b62000a2760408a0160208b0162003949565b6040518663ffffffff1660e01b815260040162000a4995949392919062003fd0565b600060405180830381600087803b15801562000a6457600080fd5b505af115801562000a79573d6000803e3d6000fd5b5062000a8d92505050602085018562003f6c565b600281111562000aa15762000aa162003f8a565b600f5460405173ffffffffffffffffffffffffffffffffffffffff888116825263ffffffff909216918916907f18f1b5b2f2896ceca69a279113b7af0a7e960d1110ad956f0a74524e2b3dfa5b9060200160405180910390a462000b09602084018462003f6c565b600281111562000b1d5762000b1d62003f8a565b60405173ffffffffffffffffffffffffffffffffffffffff87811682528816907f7fe059d890ebde22fe5020cd6cac7410b48625c597cbc52f3e6c91f3c59f7c389060200160405180910390a3505050505050565b60005473ffffffffffffffffffffffffffffffffffffffff16331462000bc4576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6103e881111562000c01576040517f398d4d3200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600e81905560405181907f267f35adfb1c3a400666ab8eb7414e5d1cda481952b8b8fcad28d003a7874c6890600090a250565b600073dceac80c8429140450b45697aae1ccb8dc921ccb6337404955601060008562000c61578862000c6b565b600f5463ffffffff165b63ffffffff1663ffffffff168152602001908152602001600020600062000c92876200286a565b815260200190815260200160002054866040518363ffffffff1660e01b815260040162000cdf92919091825273ffffffffffffffffffffffffffffffffffffffff16602082015260400190565b602060405180830381865af415801562000cfd573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000d23919062004028565b95945050505050565b63ffffffff841660009081526012602052604081208162000d4f8686866200289c565b815260208101919091526040016000205460ff1695945050505050565b600062000d7933620028d4565b6040517f24703dbc00000000000000000000000000000000000000000000000000000000815290915073ffffffffffffffffffffffffffffffffffffffff8216906324703dbc9062000dd4908790879087906004016200409a565b600060405180830381600087803b15801562000def57600080fd5b505af115801562000e04573d6000803e3d6000fd5b5050505050505050565b336000908152600160205260409020547f97667070c54ef182b0f5858b034beac1b6f3089aa2d3188bb1e8929f4fa9b9291462000e77576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6060810135158062000e99575062000e97600f5463ffffffff16600b1490565b155b1562000ed1576040517f398d4d3200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b62000edd82826200261f565b600062000eea83620024d0565b6040517fbc0348e50000000000000000000000000000000000000000000000000000000081526060840135600482015290915073ffffffffffffffffffffffffffffffffffffffff82169063bc0348e590602401600060405180830381600087803b15801562000f5957600080fd5b505af115801562000f6e573d6000803e3d6000fd5b50505050505050565b62000f82876200293a565b6002805473ffffffffffffffffffffffffffffffffffffffff9788167fffffffffffffffffffffffff00000000000000000000000000000000000000009182161790915560038054968816968216969096179095556004805494871694861694909417909355600680549286169290941691909117909255600f805491909316640100000000027fffffffffffffffff00000000000000000000000000000000000000000000000090911663ffffffff9092169190911717905550565b60005473ffffffffffffffffffffffffffffffffffffffff16331462001091576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600280547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83169081179091556040517fb08982e7405e1df5c0f03355316cd1611d894a67dbaed99e5fa87307f54f171190600090a250565b60006200110d89620024d0565b600f54909150620011279063ffffffff1687898b6200254e565b6000829003620012185773ffffffffffffffffffffffffffffffffffffffff8916331462001181576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f79866c5b00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8216906379866c5b90620011de908b908b908b908b90600090600401620040cc565b600060405180830381600087803b158015620011f957600080fd5b505af11580156200120e573d6000803e3d6000fd5b50505050620012f2565b620012608984848080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152508d92508b91508a90508962002ace565b6040517f79866c5b00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8216906379866c5b90620012bd908b908b908b908b90600190600401620040cc565b600060405180830381600087803b158015620012d857600080fd5b505af1158015620012ed573d6000803e3d6000fd5b505050505b505050505050505050565b60005473ffffffffffffffffffffffffffffffffffffffff1633146200134f576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b86851415806200135f5750868314155b806200136b5750868114155b15620013a3576040517f7db491eb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8660005b81811015620015d1576000620014408b8b84818110620013cb57620013cb62004119565b9050602002016020810190620013e2919062003949565b8a8a85818110620013f757620013f762004119565b90506020020160208101906200140e919062003f6c565b89898681811062001423576200142362004119565b90506020020160208101906200143a919062003949565b6200289c565b905084848381811062001457576200145762004119565b90506020020160208101906200146e919062004148565b63ffffffff8d166000908152601260209081526040808320858452909152902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169115159190911790558a8a83818110620014d057620014d062004119565b9050602002016020810190620014e7919062003949565b73ffffffffffffffffffffffffffffffffffffffff167f5310df464b3c7a93f2147cf6f6d5eec46c7b7be22fc612af20e6f1a301e1f6088d8b8b8681811062001534576200153462004119565b90506020020160208101906200154b919062003f6c565b8a8a8781811062001560576200156062004119565b905060200201602081019062001577919062003949565b8989888181106200158c576200158c62004119565b9050602002016020810190620015a3919062004148565b604051620015b5949392919062004168565b60405180910390a250620015c981620041e0565b9050620013a7565b5050505050505050505050565b60005473ffffffffffffffffffffffffffffffffffffffff16331462001630576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8116620016d9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602c60248201527f416363657373436f6e74726f6c3a206e65772061646d696e206973207468652060448201527f7a65726f2061646472657373000000000000000000000000000000000000000060648201526084015b60405180910390fd5b6000805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681178455604051919092169283917f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f9190a35050565b60005473ffffffffffffffffffffffffffffffffffffffff163314620017a0576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8483141580620017b05750848114155b15620017e8576040517f7db491eb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8460005b8181101562000e045760008888838181106200180c576200180c62004119565b90506020020160208101906200182391906200421b565b905060008787848181106200183c576200183c62004119565b905060200201602081019062001853919062004239565b905060008686858181106200186c576200186c62004119565b90506020020135905063ffffffff80168363ffffffff1603620018bb576040517f398d4d3200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b63ffffffff831660009081526010602052604081208291620018dd856200286a565b8152602081019190915260400160002055505050600101620017ec565b60005473ffffffffffffffffffffffffffffffffffffffff1633146200194c576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600755565b336000908152600160205260409020547f97667070c54ef182b0f5858b034beac1b6f3089aa2d3188bb1e8929f4fa9b92914620019ba576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600f54600654620019ec918891889163ffffffff169073ffffffffffffffffffffffffffffffffffffffff16620025df565b620019f887866200261f565b600062001a0588620028d4565b905073ffffffffffffffffffffffffffffffffffffffff811663c2543bb362001a3260208a018a62003f6c565b62001a4460408b0160208c0162003949565b888888886040518763ffffffff1660e01b815260040162001a6b96959493929190620042a0565b600060405180830381600087803b15801562001a8657600080fd5b505af115801562001a9b573d6000803e3d6000fd5b5062001aaf92505050602088018862003f6c565b600281111562001ac35762001ac362003f8a565b60065460405173ffffffffffffffffffffffffffffffffffffffff9182168152908a16907f7fe059d890ebde22fe5020cd6cac7410b48625c597cbc52f3e6c91f3c59f7c389060200160405180910390a35050505050505050565b336000908152600160205260409020547f97667070c54ef182b0f5858b034beac1b6f3089aa2d3188bb1e8929f4fa9b9291462001b87576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600062001b98848484888a62002d97565b600f5490915060009073ffffffffffffffffffffffffffffffffffffffff8316906371f6876a9063ffffffff88811691161462001bd6578662001bdc565b63ffffffff5b62001beb602088018862003f6c565b62001bfd6040890160208a0162003949565b8b6040518563ffffffff1660e01b815260040162001c1f949392919062004303565b6020604051808303816000875af115801562001c3f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062001c65919062004028565b9050808673ffffffffffffffffffffffffffffffffffffffff168873ffffffffffffffffffffffffffffffffffffffff167f5548c837ab068cf56a2c2479df0882a4922fd203edb7517321831d95078c5f6260405160405180910390a450505050505050565b60005473ffffffffffffffffffffffffffffffffffffffff16331462001d1d576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff811662001dc2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f416363657373436f6e74726f6c3a206f70657261746f7220697320746865207a60448201527f65726f20616464726573730000000000000000000000000000000000000000006064820152608401620016d0565b73ffffffffffffffffffffffffffffffffffffffff81166000818152600160205260408082207f97667070c54ef182b0f5858b034beac1b6f3089aa2d3188bb1e8929f4fa9b9299055517fac6fa858e9350a46cec16539926e0fde25b7629f84b5a72bffaae4df888ae86d9190a250565b336000908152600160205260409020547f97667070c54ef182b0f5858b034beac1b6f3089aa2d3188bb1e8929f4fa9b9291462001e9c576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600062001ead858484898b62002d97565b90506000808062001f028962001ec760808a018a6200434e565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525062002e3f92505050565b600f54929550909350915073ffffffffffffffffffffffffffffffffffffffff85169063368815ca9063ffffffff8b811691161462001f42578962001f48565b63ffffffff5b62001f5760208a018a62003f6c565b62001f6960408b0160208c0162003949565b8e8c600001358d602001358e604001358f606001358c8c8c6040518c63ffffffff1660e01b815260040162001fa99b9a99989796959493929190620043b6565b600060405180830381600087803b15801562001fc457600080fd5b505af115801562001fd9573d6000803e3d6000fd5b50506040518935925073ffffffffffffffffffffffffffffffffffffffff808d1692508d16907fa87989ef6d00dca941c1e1d69df9aab1058d404c2a8996492375a61f1d1d3b4990600090a450505050505050505050565b60005473ffffffffffffffffffffffffffffffffffffffff16331462002083576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8116600081815260016020526040808220829055517f80c0b871b97b595b16a7741c1b06fed0c6f6f558639f18ccbce50724325dc40d9190a250565b6000620020e133620028d4565b6040517fac98d9b000000000000000000000000000000000000000000000000000000000815290915073ffffffffffffffffffffffffffffffffffffffff82169063ac98d9b09062002140908990899089908990899060040162004481565b600060405180830381600087803b1580156200215b57600080fd5b505af115801562002170573d6000803e3d6000fd5b50505050505050505050565b6000600e54600014806200218e575081155b80620021a85750620021a8600f5463ffffffff16600b1490565b15620021b657506000919050565b620186a0600e5483620021ca9190620044ce565b620021d69190620044e8565b92915050565b6000620021e933620028d4565b6040517fe9e833e600000000000000000000000000000000000000000000000000000000815290915073ffffffffffffffffffffffffffffffffffffffff82169063e9e833e6906200224c908b908b908b908b908b908b908b9060040162004524565b600060405180830381600087803b1580156200226757600080fd5b505af11580156200227c573d6000803e3d6000fd5b505050505050505050505050565b336000908152600160205260409020547f97667070c54ef182b0f5858b034beac1b6f3089aa2d3188bb1e8929f4fa9b92914620022f3576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006200230087620024d0565b90506200230d8662002ea2565b600f546200235b9063ffffffff166200232d604087016020880162003949565b6200233c602088018862003f6c565b60065473ffffffffffffffffffffffffffffffffffffffff166200254e565b600654620023859084908490899073ffffffffffffffffffffffffffffffffffffffff16620025df565b6200239187836200261f565b600654620023b790829073ffffffffffffffffffffffffffffffffffffffff166200276a565b73ffffffffffffffffffffffffffffffffffffffff81166336fd374586620023e3602088018862003f6c565b620023f56040890160208a0162003949565b8a6040518563ffffffff1660e01b815260040162002417949392919062004651565b600060405180830381600087803b1580156200243257600080fd5b505af115801562002447573d6000803e3d6000fd5b506200245b92505050602085018562003f6c565b60028111156200246f576200246f62003f8a565b60065460405173ffffffffffffffffffffffffffffffffffffffff918216815263ffffffff8916918a16907f18f1b5b2f2896ceca69a279113b7af0a7e960d1110ad956f0a74524e2b3dfa5b9060200160405180910390a450505050505050565b73ffffffffffffffffffffffffffffffffffffffff808216600090815260086020526040902054168062002549576040517f2b1991ae00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff83166004820152602401620016d0565b919050565b63ffffffff8416600090815260126020526040812081620025718686866200289c565b815260208101919091526040016000205460ff16905080620025d8576040517fb1b1f01400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85166004820152602401620016d0565b5050505050565b620025ec84848462002f30565b620026198262002603604087016020880162003949565b62002612602088018862003f6c565b846200254e565b50505050565b60006060820135156200268f5762002638828062004698565b62002647602085018562004698565b62002656604087018762004698565b876060013560405160200162002673979695949392919062004780565b60405160208183030381529060405280519060200120620026e7565b6200269b828062004698565b620026aa602085018562004698565b620026b9604087018762004698565b604051602001620026d096959493929190620047d7565b604051602081830303815290604052805190602001205b7f19457468657265756d205369676e6564204d6573736167653a0a3332000000006000908152601c829052603c902090915062002619846200272d60808601866200434e565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525086925062003170915050565b73ffffffffffffffffffffffffffffffffffffffff808316600090815260116020908152604080832093851683529290522054600754620027ac908262004819565b42101562002801574260075482620027c5919062004819565b6040517f4be8f90500000000000000000000000000000000000000000000000000000000815260048101929092526024820152604401620016d0565b73ffffffffffffffffffffffffffffffffffffffff80841660008181526011602090815260408083209487168084529490915280822042908190559051909392917fc78e367d3ff6f614ce683fcce3c77bc551c69a0db175f03efedbd4dd4080590991a4505050565b6000816040516020016200287f91906200482f565b604051602081830303815290604052805190602001209050919050565b6000838383604051602001620028b59392919062004853565b6040516020818303038152906040528051906020012090509392505050565b73ffffffffffffffffffffffffffffffffffffffff8181166000908152600860205260408120549091166200290e57620021d6826200325b565b5073ffffffffffffffffffffffffffffffffffffffff9081166000908152600860205260409020541690565b60005473ffffffffffffffffffffffffffffffffffffffff1615620029e2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602860248201527f416363657373436f6e74726f6c3a2061646d696e20616c726561647920696e6960448201527f7469616c697a65640000000000000000000000000000000000000000000000006064820152608401620016d0565b73ffffffffffffffffffffffffffffffffffffffff811662002a87576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602860248201527f416363657373436f6e74726f6c3a2061646d696e20697320746865207a65726f60448201527f20616464726573730000000000000000000000000000000000000000000000006064820152608401620016d0565b600080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b8042111562002b0d576040517f277b2d7400000000000000000000000000000000000000000000000000000000815260048101829052602401620016d0565b600c8560405162002b1f9190620048d9565b9081526040519081900360200190205460ff161562002b6e57846040517fbf42d003000000000000000000000000000000000000000000000000000000008152600401620016d0919062004943565b6001600c8660405162002b829190620048d9565b90815260408051918290036020908101832080549415157fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00909516949094179093557fc4c90a8c884e43804bc3e7d7a39f8a2f6bbfa0212985c48e240d628c228f04608383015273ffffffffffffffffffffffffffffffffffffffff87811683830152861660608301526080820185905260a08083018590528151808403909101815260c090920190528051910120600062002d37604080518082018252601481527f4d6574614c656e6420526562616c616e63696e6700000000000000000000000060209182015281518083018352600581527f332e302e300000000000000000000000000000000000000000000000000000009082015281517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f818301527f85d7f6d19ce33abcd38ffd3d658144a573976d214de6c9a3ee824fff292c8d31818401527fd7a1ce683065975771bedf401ecab037f4f4c62cc51fefdc8b39dd246ff0343a60608201524660808201523060a0808301919091528351808303909101815260c0909101909252815191012090565b6040517f1901000000000000000000000000000000000000000000000000000000000000602082015260228101919091526042810183905260620160405160208183030381529060405280519060200120905062000e0488888362003170565b600062002da785858885620025df565b62002db383856200261f565b600f5463ffffffff87811691161462002e265762002dd18662002ea2565b60065473ffffffffffffffffffffffffffffffffffffffff83811691161462002e26576040517f398d4d3200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b62002e3183620028d4565b905062000d2381836200341c565b6000806000835160411462002e865784846040517f50101a47000000000000000000000000000000000000000000000000000000008152600401620016d092919062004958565b5050506020810151604082015160609092015160001a93909250565b600f5463ffffffff8281169116148062002eec575063ffffffff811660009081526010602052604081209062002ed960046200286a565b8152602001908152602001600020546000145b1562002f2d576040517fd3616c3700000000000000000000000000000000000000000000000000000000815263ffffffff82166004820152602401620016d0565b50565b62002f3f602083018362004698565b905062002f4d838062004698565b905014158062002f7a575062002f67604083018362004698565b905062002f75838062004698565b905014155b1562002fb2576040517f7db491eb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600062002fc0838062004698565b9050905060005b818110156200313d57600062002fde858062004698565b8381811062002ff15762002ff162004119565b905060200201602081019062003008919062003f6c565b905060006200301b602087018762004698565b848181106200302e576200302e62004119565b905060200201602081019062003045919062003949565b9050600062003058604088018862004698565b858181106200306b576200306b62004119565b90506020020160208101906200308291906200421b565b905062003093602089018962003f6c565b6002811115620030a757620030a762003f8a565b836002811115620030bc57620030bc62003f8a565b148015620031065750620030d76040890160208a0162003949565b73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16145b80156200311e57508563ffffffff168163ffffffff16145b156200312e575050505050505050565b83600101935050505062002fc7565b506040517f398d4d3200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600f546040517f98ef1ed800000000000000000000000000000000000000000000000000000000815264010000000090910473ffffffffffffffffffffffffffffffffffffffff16906398ef1ed890620031d39086908590879060040162004991565b6020604051808303816000875af1158015620031f3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620032199190620049c8565b620032565782826040517f50101a47000000000000000000000000000000000000000000000000000000008152600401620016d092919062004958565b505050565b600080604051806020016200327090620035b1565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe082820381018352601f90910116604081815230602083015273ffffffffffffffffffffffffffffffffffffffff8616908201529091506000908290606001604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152908290526200330d9291602001620049e8565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152908290527fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606087901b16602083015291506000906034016040516020818303038152906040528051906020012090506200339660008284620034ce565b73ffffffffffffffffffffffffffffffffffffffff86811660008181526008602052604080822080547fffffffffffffffffffffffff000000000000000000000000000000000000000016948616948517905551939750919290917f3b2ac627f2dda3cc16038f312aeeda34474e3770c24737309735bb3911bdcf8291a3505050919050565b60075473ffffffffffffffffffffffffffffffffffffffff8084166000908152601160209081526040808320938616835292905220546200345e919062004819565b421015620034ca5773ffffffffffffffffffffffffffffffffffffffff80831660008181526011602090815260408083209486168084529490915280822082905551909291907fc78e367d3ff6f614ce683fcce3c77bc551c69a0db175f03efedbd4dd40805909908490a45b5050565b60008347101562003515576040517fe4bbecac00000000000000000000000000000000000000000000000000000000815247600482015260248101859052604401620016d0565b815160000362003551576040517f4ca249dc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8282516020840186f5905073ffffffffffffffffffffffffffffffffffffffff8116620035aa576040517f741752c200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b9392505050565b6102138062004a1c83390190565b803573ffffffffffffffffffffffffffffffffffffffff811681146200254957600080fd5b600060408284031215620035f757600080fd5b50919050565b600060a08284031215620035f757600080fd5b600080600080600060e086880312156200362957600080fd5b6200363486620035bf565b94506200364460208701620035bf565b9350620036558760408801620035e4565b9250620036668760808801620035e4565b915060c086013567ffffffffffffffff8111156200368357600080fd5b6200369188828901620035fd565b9150509295509295909350565b600060208284031215620036b157600080fd5b5035919050565b803563ffffffff811681146200254957600080fd5b8035600681106200254957600080fd5b801515811462002f2d57600080fd5b600080600080608085870312156200370357600080fd5b6200370e85620036b8565b93506200371e60208601620035bf565b92506200372e60408601620036cd565b915060608501356200374081620036dd565b939692955090935050565b8035600381106200254957600080fd5b600080600080608085870312156200377257600080fd5b6200377d85620036b8565b93506200378d60208601620035bf565b92506200379d604086016200374b565b9150620037ad60608601620035bf565b905092959194509250565b60008083601f840112620037cb57600080fd5b50813567ffffffffffffffff811115620037e457600080fd5b6020830191508360208260051b85010111156200380057600080fd5b9250929050565b6000806000604084860312156200381d57600080fd5b6200382884620035bf565b9250602084013567ffffffffffffffff8111156200384557600080fd5b6200385386828701620037b8565b9497909650939450505050565b600080604083850312156200387457600080fd5b6200387f83620035bf565b9150602083013567ffffffffffffffff8111156200389c57600080fd5b620038aa85828601620035fd565b9150509250929050565b600080600080600080600060e0888a031215620038d057600080fd5b620038db88620035bf565b9650620038eb60208901620035bf565b9550620038fb60408901620035bf565b94506200390b60608901620035bf565b93506200391b60808901620035bf565b92506200392b60a08901620036b8565b91506200393b60c08901620035bf565b905092959891949750929550565b6000602082840312156200395c57600080fd5b620035aa82620035bf565b60008083601f8401126200397a57600080fd5b50813567ffffffffffffffff8111156200399357600080fd5b6020830191508360208285010111156200380057600080fd5b60008060008060008060008060e0898b031215620039c957600080fd5b620039d489620035bf565b9750620039e460208a01620035bf565b9650620039f460408a016200374b565b955062003a0460608a01620035bf565b94506080890135935060a0890135925060c089013567ffffffffffffffff81111562003a2f57600080fd5b62003a3d8b828c0162003967565b999c989b5096995094979396929594505050565b600080600080600080600080600060a08a8c03121562003a7057600080fd5b62003a7b8a620036b8565b985060208a013567ffffffffffffffff8082111562003a9957600080fd5b62003aa78d838e01620037b8565b909a50985060408c013591508082111562003ac157600080fd5b62003acf8d838e01620037b8565b909850965060608c013591508082111562003ae957600080fd5b62003af78d838e01620037b8565b909650945060808c013591508082111562003b1157600080fd5b5062003b208c828d01620037b8565b915080935050809150509295985092959850929598565b6000806000806000806060878903121562003b5157600080fd5b863567ffffffffffffffff8082111562003b6a57600080fd5b62003b788a838b01620037b8565b9098509650602089013591508082111562003b9257600080fd5b62003ba08a838b01620037b8565b9096509450604089013591508082111562003bba57600080fd5b5062003bc989828a01620037b8565b979a9699509497509295939492505050565b600080600080600080600060c0888a03121562003bf757600080fd5b62003c0288620035bf565b965062003c138960208a01620035e4565b9550606088013567ffffffffffffffff8082111562003c3157600080fd5b62003c3f8b838c01620035fd565b965060808a013591508082111562003c5657600080fd5b62003c648b838c0162003967565b909650945060a08a013591508082111562003c7e57600080fd5b5062003c8d8a828b0162003967565b989b979a50959850939692959293505050565b600080600080600060c0868803121562003cb957600080fd5b62003cc486620035bf565b945062003cd460208701620035bf565b935062003ce460408701620036b8565b925062003cf58760608801620035e4565b915060a086013567ffffffffffffffff8111156200368357600080fd5b60008060008060008060e0878903121562003d2c57600080fd5b62003d3787620035bf565b955062003d4760208801620035bf565b945062003d5760408801620036b8565b9350606087013567ffffffffffffffff8082111562003d7557600080fd5b62003d838a838b01620035fd565b945062003d948a60808b01620035e4565b935060c089013591508082111562003dab57600080fd5b5062003dba89828a01620035fd565b9150509295509295509295565b60008060008060006080868803121562003de057600080fd5b62003deb86620035bf565b945062003dfb60208701620035bf565b935060408601359250606086013567ffffffffffffffff81111562003e1f57600080fd5b62003e2d88828901620037b8565b969995985093965092949392505050565b60008060008060008060006080888a03121562003e5a57600080fd5b62003e6588620035bf565b9650602088013567ffffffffffffffff8082111562003e8357600080fd5b62003e918b838c01620037b8565b909850965060408a013591508082111562003eab57600080fd5b62003eb98b838c01620037b8565b909650945060608a013591508082111562003ed357600080fd5b5062003c8d8a828b01620037b8565b600080600080600080610100878903121562003efd57600080fd5b62003f0887620035bf565b955062003f1860208801620036b8565b94506040870135935062003f308860608901620035e4565b925062003f418860a08901620035e4565b915060e087013567ffffffffffffffff81111562003f5e57600080fd5b62003dba89828a01620035fd565b60006020828403121562003f7f57600080fd5b620035aa826200374b565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b6003811062003fcc5762003fcc62003f8a565b9052565b73ffffffffffffffffffffffffffffffffffffffff868116825260a082019062003ffe602084018862003fb9565b808616604084015262004015606084018662003fb9565b8084166080840152509695505050505050565b6000602082840312156200403b57600080fd5b5051919050565b8183526000602080850194508260005b858110156200408f5773ffffffffffffffffffffffffffffffffffffffff6200407b83620035bf565b168752958201959082019060010162004052565b509495945050505050565b73ffffffffffffffffffffffffffffffffffffffff8416815260406020820152600062000d2360408301848662004042565b73ffffffffffffffffffffffffffffffffffffffff868116825260a0820190620040fa602084018862003fb9565b9490941660408201526060810192909252151560809091015292915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b6000602082840312156200415b57600080fd5b8135620035aa81620036dd565b63ffffffff851681526080810162004184602083018662003fb9565b73ffffffffffffffffffffffffffffffffffffffff84166040830152821515606083015295945050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203620042145762004214620041b1565b5060010190565b6000602082840312156200422e57600080fd5b620035aa82620036b8565b6000602082840312156200424c57600080fd5b620035aa82620036cd565b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b620042ac818862003fb9565b73ffffffffffffffffffffffffffffffffffffffff86166020820152608060408201526000620042e160808301868862004257565b8281036060840152620042f681858762004257565b9998505050505050505050565b63ffffffff85168152608081016200431f602083018662003fb9565b73ffffffffffffffffffffffffffffffffffffffff808516604084015280841660608401525095945050505050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126200438457600080fd5b83018035915067ffffffffffffffff821115620043a057600080fd5b6020019150368190038213156200380057600080fd5b63ffffffff8c1681526101608101620043d3602083018d62003fb9565b73ffffffffffffffffffffffffffffffffffffffff9a8b1660408301529890991660608a0152608089019690965260a088019490945260c087019290925260e086015260ff166101008501526101208401526101409092019190915292915050565b81835260007f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8311156200446857600080fd5b8260051b80836020870137939093016020019392505050565b600073ffffffffffffffffffffffffffffffffffffffff808816835280871660208401525084604083015260806060830152620044c360808301848662004435565b979650505050505050565b8082028115828204841417620021d657620021d6620041b1565b6000826200451f577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b73ffffffffffffffffffffffffffffffffffffffff88168152600060206080818401526200455760808401898b62004042565b83810360408501526200456c81888a62004435565b84810360608601528581529050818101600586811b830184018860005b898110156200463d577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe086840301855281357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18c3603018112620045ec57600080fd5b8b01878101903567ffffffffffffffff8111156200460957600080fd5b80861b36038213156200461b57600080fd5b6200462885828462004435565b96890196945050509086019060010162004589565b50909e9d5050505050505050505050505050565b8481526080810162004667602083018662003fb9565b73ffffffffffffffffffffffffffffffffffffffff8416604083015263ffffffff8316606083015295945050505050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112620046ce57600080fd5b83018035915067ffffffffffffffff821115620046ea57600080fd5b6020019150600581901b36038213156200380057600080fd5b8183526000602080850194508260005b858110156200408f5762004732876200472c846200374b565b62003fb9565b958201959082019060010162004713565b8183526000602080850194508260005b858110156200408f5763ffffffff6200476c83620036b8565b168752958201959082019060010162004753565b6080815260006200479660808301898b62004703565b8281036020840152620047ab81888a62004042565b90508281036040840152620047c281868862004743565b91505082606083015298975050505050505050565b606081526000620047ed60608301888a62004703565b82810360208401526200480281878962004042565b90508281036040840152620042f681858762004743565b80820180821115620021d657620021d6620041b1565b60006006831062004844576200484462003f8a565b5060f89190911b815260010190565b60007fffffffffffffffffffffffffffffffffffffffff000000000000000000000000808660601b1683526003851062004891576200489162003f8a565b60f89490941b60148301525060609190911b9091166015820152602901919050565b60005b83811015620048d0578181015183820152602001620048b6565b50506000910152565b60008251620048ed818460208701620048b3565b9190910192915050565b6000815180845262004911816020860160208601620048b3565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b602081526000620035aa6020830184620048f7565b73ffffffffffffffffffffffffffffffffffffffff83168152604060208201526000620049896040830184620048f7565b949350505050565b73ffffffffffffffffffffffffffffffffffffffff8416815282602082015260606040820152600062000d236060830184620048f7565b600060208284031215620049db57600080fd5b8151620035aa81620036dd565b60008351620049fc818460208801620048b3565b83519083019062004a12818360208801620048b3565b0194935050505056fe608060405234801561001057600080fd5b5060405161021338038061021383398101604081905261002f916100ac565b600080546001600160a01b038085166001600160a01b03199283168117845560018054928616929093168217909255604051909230917f5de57f6bdaa5661f65214dfcfe60688ade6759f45b42d2db77b8139e0571e9089190a450506100df565b80516001600160a01b03811681146100a757600080fd5b919050565b600080604083850312156100bf57600080fd5b6100c883610090565b91506100d660208401610090565b90509250929050565b610125806100ee6000396000f3fe608060405261000c61000e565b005b61001e610019610020565b6100b7565b565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663a9fda0eb6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561008e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906100b291906100db565b905090565b3660008037600080366000845af43d6000803e8080156100d6573d6000f35b3d6000fd5b6000602082840312156100ed57600080fd5b815173ffffffffffffffffffffffffffffffffffffffff8116811461011157600080fd5b939250505056fea164736f6c6343000814000aa164736f6c6343000814000a
Deployed Bytecode
0x60806040523480156200001157600080fd5b5060043610620003195760003560e01c8063923ca9a411620001a5578063b3de272d11620000f5578063db1c4ded11620000a3578063f95aea4c116200007a578063f95aea4c1462000803578063fa62ebaa146200081a578063fdfd5a25146200083957600080fd5b8063db1c4ded14620007a8578063e9e833e614620007c3578063eb39e9ba14620007da57600080fd5b8063b68a659211620000d8578063b68a6592146200076b578063bd2f7172146200078a578063d73792a9146200079d57600080fd5b8063b3de272d146200074a578063b5fa9dd2146200075457600080fd5b8063a44d57a01162000153578063ab08c5241162000136578063ab08c5241462000705578063ac8a584a146200071c578063ac98d9b0146200073357600080fd5b8063a44d57a014620006ad578063a9fda0eb14620006e657600080fd5b80639358a04111620001885780639358a041146200065c5780639870d7fe146200067357806399374642146200068a57600080fd5b8063923ca9a414620005e857806392765861146200064557600080fd5b8063468f8f52116200026d5780636e9960c3116200021b5780638057739911620001f25780638057739914620005a157806381ca52cf14620005c857806382e5bec314620005df57600080fd5b80636e9960c31462000554578063704b6c0214620005735780637e22e6eb146200058a57600080fd5b806351f0caef116200025057806351f0caef14620004d85780636aa9541414620004e25780636d70f7ae14620004f957600080fd5b8063468f8f5214620004aa57806351c2569814620004c157600080fd5b806324703dbc11620002cb5780633b44390a11620002ae5780633b44390a14620003f65780633f1b7020146200043657806344f2c5aa146200044d57600080fd5b806324703dbc14620003c85780633acbc1e914620003df57600080fd5b80630c3ee3ac11620003005780630c3ee3ac146200034e5780631ffe1422146200037857806320606b7014620003a057600080fd5b80630538153d146200031e578063091373601462000337575b600080fd5b620003356200032f36600462003610565b62000861565b005b62000335620003483660046200369e565b62000b72565b620003656200035f366004620036ec565b62000c34565b6040519081526020015b60405180910390f35b6200038f620003893660046200375b565b62000d2c565b60405190151581526020016200036f565b620003657f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f81565b62000335620003d936600462003807565b62000d6c565b62000335620003f036600462003860565b62000e0e565b60065473ffffffffffffffffffffffffffffffffffffffff165b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016200036f565b6200033562000447366004620038b4565b62000f77565b60408051808201909152601481527f4d6574614c656e6420526562616c616e63696e67000000000000000000000000602090910152620003657f85d7f6d19ce33abcd38ffd3d658144a573976d214de6c9a3ee824fff292c8d3181565b62000335620004bb36600462003949565b6200103f565b62000335620004d2366004620039ac565b62001100565b620003656103e881565b62000335620004f336600462003a51565b620012fd565b6200038f6200050a36600462003949565b73ffffffffffffffffffffffffffffffffffffffff166000908152600160205260409020547f97667070c54ef182b0f5858b034beac1b6f3089aa2d3188bb1e8929f4fa9b9291490565b60005473ffffffffffffffffffffffffffffffffffffffff1662000410565b620003356200058436600462003949565b620015de565b620003356200059b36600462003b37565b6200174e565b600f54620005b29063ffffffff1681565b60405163ffffffff90911681526020016200036f565b62000335620005d93660046200369e565b620018fa565b620005b2600b81565b60408051808201909152600581527f332e302e30000000000000000000000000000000000000000000000000000000602090910152620003657fd7a1ce683065975771bedf401ecab037f4f4c62cc51fefdc8b39dd246ff0343a81565b620003356200065636600462003bdb565b62001951565b620003356200066d36600462003ca0565b62001b1e565b620003356200068436600462003949565b62001ccb565b620003656200069b36600462003949565b60016020526000908152604090205481565b62000410620006be36600462003949565b60086020526000908152604090205473ffffffffffffffffffffffffffffffffffffffff1681565b60025473ffffffffffffffffffffffffffffffffffffffff1662000410565b620003356200071636600462003d12565b62001e33565b620003356200072d36600462003949565b62002031565b620003356200074436600462003dc7565b620020d4565b6200036560075481565b62000365620007653660046200369e565b6200217c565b60045473ffffffffffffffffffffffffffffffffffffffff1662000410565b6200038f600f5463ffffffff16600b1490565b62000365620186a081565b73374d7860c4f2f604de0191298dd393703cce84f362000410565b62000335620007d436600462003e3e565b620021dc565b600f546200041090640100000000900473ffffffffffffffffffffffffffffffffffffffff1681565b620003356200081436600462003ee2565b6200228a565b60035473ffffffffffffffffffffffffffffffffffffffff1662000410565b620003657fc4c90a8c884e43804bc3e7d7a39f8a2f6bbfa0212985c48e240d628c228f046081565b336000908152600160205260409020547f97667070c54ef182b0f5858b034beac1b6f3089aa2d3188bb1e8929f4fa9b92914620008ca576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000620008d786620024d0565b9050620008eb604084016020850162003949565b73ffffffffffffffffffffffffffffffffffffffff1662000913604086016020870162003949565b73ffffffffffffffffffffffffffffffffffffffff160362000961576040517f398d4d3200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600f54620009979063ffffffff1662000981604087016020880162003949565b62000990602088018862003f6c565b886200254e565b600f54620009b0908490849063ffffffff1688620025df565b620009bc86836200261f565b620009c881866200276a565b73ffffffffffffffffffffffffffffffffffffffff811663fe4c85bc86620009f4602088018862003f6c565b62000a066040890160208a0162003949565b62000a15602089018962003f6c565b62000a2760408a0160208b0162003949565b6040518663ffffffff1660e01b815260040162000a4995949392919062003fd0565b600060405180830381600087803b15801562000a6457600080fd5b505af115801562000a79573d6000803e3d6000fd5b5062000a8d92505050602085018562003f6c565b600281111562000aa15762000aa162003f8a565b600f5460405173ffffffffffffffffffffffffffffffffffffffff888116825263ffffffff909216918916907f18f1b5b2f2896ceca69a279113b7af0a7e960d1110ad956f0a74524e2b3dfa5b9060200160405180910390a462000b09602084018462003f6c565b600281111562000b1d5762000b1d62003f8a565b60405173ffffffffffffffffffffffffffffffffffffffff87811682528816907f7fe059d890ebde22fe5020cd6cac7410b48625c597cbc52f3e6c91f3c59f7c389060200160405180910390a3505050505050565b60005473ffffffffffffffffffffffffffffffffffffffff16331462000bc4576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6103e881111562000c01576040517f398d4d3200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600e81905560405181907f267f35adfb1c3a400666ab8eb7414e5d1cda481952b8b8fcad28d003a7874c6890600090a250565b600073dceac80c8429140450b45697aae1ccb8dc921ccb6337404955601060008562000c61578862000c6b565b600f5463ffffffff165b63ffffffff1663ffffffff168152602001908152602001600020600062000c92876200286a565b815260200190815260200160002054866040518363ffffffff1660e01b815260040162000cdf92919091825273ffffffffffffffffffffffffffffffffffffffff16602082015260400190565b602060405180830381865af415801562000cfd573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000d23919062004028565b95945050505050565b63ffffffff841660009081526012602052604081208162000d4f8686866200289c565b815260208101919091526040016000205460ff1695945050505050565b600062000d7933620028d4565b6040517f24703dbc00000000000000000000000000000000000000000000000000000000815290915073ffffffffffffffffffffffffffffffffffffffff8216906324703dbc9062000dd4908790879087906004016200409a565b600060405180830381600087803b15801562000def57600080fd5b505af115801562000e04573d6000803e3d6000fd5b5050505050505050565b336000908152600160205260409020547f97667070c54ef182b0f5858b034beac1b6f3089aa2d3188bb1e8929f4fa9b9291462000e77576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6060810135158062000e99575062000e97600f5463ffffffff16600b1490565b155b1562000ed1576040517f398d4d3200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b62000edd82826200261f565b600062000eea83620024d0565b6040517fbc0348e50000000000000000000000000000000000000000000000000000000081526060840135600482015290915073ffffffffffffffffffffffffffffffffffffffff82169063bc0348e590602401600060405180830381600087803b15801562000f5957600080fd5b505af115801562000f6e573d6000803e3d6000fd5b50505050505050565b62000f82876200293a565b6002805473ffffffffffffffffffffffffffffffffffffffff9788167fffffffffffffffffffffffff00000000000000000000000000000000000000009182161790915560038054968816968216969096179095556004805494871694861694909417909355600680549286169290941691909117909255600f805491909316640100000000027fffffffffffffffff00000000000000000000000000000000000000000000000090911663ffffffff9092169190911717905550565b60005473ffffffffffffffffffffffffffffffffffffffff16331462001091576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600280547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83169081179091556040517fb08982e7405e1df5c0f03355316cd1611d894a67dbaed99e5fa87307f54f171190600090a250565b60006200110d89620024d0565b600f54909150620011279063ffffffff1687898b6200254e565b6000829003620012185773ffffffffffffffffffffffffffffffffffffffff8916331462001181576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f79866c5b00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8216906379866c5b90620011de908b908b908b908b90600090600401620040cc565b600060405180830381600087803b158015620011f957600080fd5b505af11580156200120e573d6000803e3d6000fd5b50505050620012f2565b620012608984848080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152508d92508b91508a90508962002ace565b6040517f79866c5b00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8216906379866c5b90620012bd908b908b908b908b90600190600401620040cc565b600060405180830381600087803b158015620012d857600080fd5b505af1158015620012ed573d6000803e3d6000fd5b505050505b505050505050505050565b60005473ffffffffffffffffffffffffffffffffffffffff1633146200134f576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b86851415806200135f5750868314155b806200136b5750868114155b15620013a3576040517f7db491eb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8660005b81811015620015d1576000620014408b8b84818110620013cb57620013cb62004119565b9050602002016020810190620013e2919062003949565b8a8a85818110620013f757620013f762004119565b90506020020160208101906200140e919062003f6c565b89898681811062001423576200142362004119565b90506020020160208101906200143a919062003949565b6200289c565b905084848381811062001457576200145762004119565b90506020020160208101906200146e919062004148565b63ffffffff8d166000908152601260209081526040808320858452909152902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169115159190911790558a8a83818110620014d057620014d062004119565b9050602002016020810190620014e7919062003949565b73ffffffffffffffffffffffffffffffffffffffff167f5310df464b3c7a93f2147cf6f6d5eec46c7b7be22fc612af20e6f1a301e1f6088d8b8b8681811062001534576200153462004119565b90506020020160208101906200154b919062003f6c565b8a8a8781811062001560576200156062004119565b905060200201602081019062001577919062003949565b8989888181106200158c576200158c62004119565b9050602002016020810190620015a3919062004148565b604051620015b5949392919062004168565b60405180910390a250620015c981620041e0565b9050620013a7565b5050505050505050505050565b60005473ffffffffffffffffffffffffffffffffffffffff16331462001630576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8116620016d9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602c60248201527f416363657373436f6e74726f6c3a206e65772061646d696e206973207468652060448201527f7a65726f2061646472657373000000000000000000000000000000000000000060648201526084015b60405180910390fd5b6000805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681178455604051919092169283917f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f9190a35050565b60005473ffffffffffffffffffffffffffffffffffffffff163314620017a0576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8483141580620017b05750848114155b15620017e8576040517f7db491eb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8460005b8181101562000e045760008888838181106200180c576200180c62004119565b90506020020160208101906200182391906200421b565b905060008787848181106200183c576200183c62004119565b905060200201602081019062001853919062004239565b905060008686858181106200186c576200186c62004119565b90506020020135905063ffffffff80168363ffffffff1603620018bb576040517f398d4d3200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b63ffffffff831660009081526010602052604081208291620018dd856200286a565b8152602081019190915260400160002055505050600101620017ec565b60005473ffffffffffffffffffffffffffffffffffffffff1633146200194c576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600755565b336000908152600160205260409020547f97667070c54ef182b0f5858b034beac1b6f3089aa2d3188bb1e8929f4fa9b92914620019ba576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600f54600654620019ec918891889163ffffffff169073ffffffffffffffffffffffffffffffffffffffff16620025df565b620019f887866200261f565b600062001a0588620028d4565b905073ffffffffffffffffffffffffffffffffffffffff811663c2543bb362001a3260208a018a62003f6c565b62001a4460408b0160208c0162003949565b888888886040518763ffffffff1660e01b815260040162001a6b96959493929190620042a0565b600060405180830381600087803b15801562001a8657600080fd5b505af115801562001a9b573d6000803e3d6000fd5b5062001aaf92505050602088018862003f6c565b600281111562001ac35762001ac362003f8a565b60065460405173ffffffffffffffffffffffffffffffffffffffff9182168152908a16907f7fe059d890ebde22fe5020cd6cac7410b48625c597cbc52f3e6c91f3c59f7c389060200160405180910390a35050505050505050565b336000908152600160205260409020547f97667070c54ef182b0f5858b034beac1b6f3089aa2d3188bb1e8929f4fa9b9291462001b87576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600062001b98848484888a62002d97565b600f5490915060009073ffffffffffffffffffffffffffffffffffffffff8316906371f6876a9063ffffffff88811691161462001bd6578662001bdc565b63ffffffff5b62001beb602088018862003f6c565b62001bfd6040890160208a0162003949565b8b6040518563ffffffff1660e01b815260040162001c1f949392919062004303565b6020604051808303816000875af115801562001c3f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062001c65919062004028565b9050808673ffffffffffffffffffffffffffffffffffffffff168873ffffffffffffffffffffffffffffffffffffffff167f5548c837ab068cf56a2c2479df0882a4922fd203edb7517321831d95078c5f6260405160405180910390a450505050505050565b60005473ffffffffffffffffffffffffffffffffffffffff16331462001d1d576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff811662001dc2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f416363657373436f6e74726f6c3a206f70657261746f7220697320746865207a60448201527f65726f20616464726573730000000000000000000000000000000000000000006064820152608401620016d0565b73ffffffffffffffffffffffffffffffffffffffff81166000818152600160205260408082207f97667070c54ef182b0f5858b034beac1b6f3089aa2d3188bb1e8929f4fa9b9299055517fac6fa858e9350a46cec16539926e0fde25b7629f84b5a72bffaae4df888ae86d9190a250565b336000908152600160205260409020547f97667070c54ef182b0f5858b034beac1b6f3089aa2d3188bb1e8929f4fa9b9291462001e9c576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600062001ead858484898b62002d97565b90506000808062001f028962001ec760808a018a6200434e565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525062002e3f92505050565b600f54929550909350915073ffffffffffffffffffffffffffffffffffffffff85169063368815ca9063ffffffff8b811691161462001f42578962001f48565b63ffffffff5b62001f5760208a018a62003f6c565b62001f6960408b0160208c0162003949565b8e8c600001358d602001358e604001358f606001358c8c8c6040518c63ffffffff1660e01b815260040162001fa99b9a99989796959493929190620043b6565b600060405180830381600087803b15801562001fc457600080fd5b505af115801562001fd9573d6000803e3d6000fd5b50506040518935925073ffffffffffffffffffffffffffffffffffffffff808d1692508d16907fa87989ef6d00dca941c1e1d69df9aab1058d404c2a8996492375a61f1d1d3b4990600090a450505050505050505050565b60005473ffffffffffffffffffffffffffffffffffffffff16331462002083576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8116600081815260016020526040808220829055517f80c0b871b97b595b16a7741c1b06fed0c6f6f558639f18ccbce50724325dc40d9190a250565b6000620020e133620028d4565b6040517fac98d9b000000000000000000000000000000000000000000000000000000000815290915073ffffffffffffffffffffffffffffffffffffffff82169063ac98d9b09062002140908990899089908990899060040162004481565b600060405180830381600087803b1580156200215b57600080fd5b505af115801562002170573d6000803e3d6000fd5b50505050505050505050565b6000600e54600014806200218e575081155b80620021a85750620021a8600f5463ffffffff16600b1490565b15620021b657506000919050565b620186a0600e5483620021ca9190620044ce565b620021d69190620044e8565b92915050565b6000620021e933620028d4565b6040517fe9e833e600000000000000000000000000000000000000000000000000000000815290915073ffffffffffffffffffffffffffffffffffffffff82169063e9e833e6906200224c908b908b908b908b908b908b908b9060040162004524565b600060405180830381600087803b1580156200226757600080fd5b505af11580156200227c573d6000803e3d6000fd5b505050505050505050505050565b336000908152600160205260409020547f97667070c54ef182b0f5858b034beac1b6f3089aa2d3188bb1e8929f4fa9b92914620022f3576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006200230087620024d0565b90506200230d8662002ea2565b600f546200235b9063ffffffff166200232d604087016020880162003949565b6200233c602088018862003f6c565b60065473ffffffffffffffffffffffffffffffffffffffff166200254e565b600654620023859084908490899073ffffffffffffffffffffffffffffffffffffffff16620025df565b6200239187836200261f565b600654620023b790829073ffffffffffffffffffffffffffffffffffffffff166200276a565b73ffffffffffffffffffffffffffffffffffffffff81166336fd374586620023e3602088018862003f6c565b620023f56040890160208a0162003949565b8a6040518563ffffffff1660e01b815260040162002417949392919062004651565b600060405180830381600087803b1580156200243257600080fd5b505af115801562002447573d6000803e3d6000fd5b506200245b92505050602085018562003f6c565b60028111156200246f576200246f62003f8a565b60065460405173ffffffffffffffffffffffffffffffffffffffff918216815263ffffffff8916918a16907f18f1b5b2f2896ceca69a279113b7af0a7e960d1110ad956f0a74524e2b3dfa5b9060200160405180910390a450505050505050565b73ffffffffffffffffffffffffffffffffffffffff808216600090815260086020526040902054168062002549576040517f2b1991ae00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff83166004820152602401620016d0565b919050565b63ffffffff8416600090815260126020526040812081620025718686866200289c565b815260208101919091526040016000205460ff16905080620025d8576040517fb1b1f01400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85166004820152602401620016d0565b5050505050565b620025ec84848462002f30565b620026198262002603604087016020880162003949565b62002612602088018862003f6c565b846200254e565b50505050565b60006060820135156200268f5762002638828062004698565b62002647602085018562004698565b62002656604087018762004698565b876060013560405160200162002673979695949392919062004780565b60405160208183030381529060405280519060200120620026e7565b6200269b828062004698565b620026aa602085018562004698565b620026b9604087018762004698565b604051602001620026d096959493929190620047d7565b604051602081830303815290604052805190602001205b7f19457468657265756d205369676e6564204d6573736167653a0a3332000000006000908152601c829052603c902090915062002619846200272d60808601866200434e565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525086925062003170915050565b73ffffffffffffffffffffffffffffffffffffffff808316600090815260116020908152604080832093851683529290522054600754620027ac908262004819565b42101562002801574260075482620027c5919062004819565b6040517f4be8f90500000000000000000000000000000000000000000000000000000000815260048101929092526024820152604401620016d0565b73ffffffffffffffffffffffffffffffffffffffff80841660008181526011602090815260408083209487168084529490915280822042908190559051909392917fc78e367d3ff6f614ce683fcce3c77bc551c69a0db175f03efedbd4dd4080590991a4505050565b6000816040516020016200287f91906200482f565b604051602081830303815290604052805190602001209050919050565b6000838383604051602001620028b59392919062004853565b6040516020818303038152906040528051906020012090509392505050565b73ffffffffffffffffffffffffffffffffffffffff8181166000908152600860205260408120549091166200290e57620021d6826200325b565b5073ffffffffffffffffffffffffffffffffffffffff9081166000908152600860205260409020541690565b60005473ffffffffffffffffffffffffffffffffffffffff1615620029e2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602860248201527f416363657373436f6e74726f6c3a2061646d696e20616c726561647920696e6960448201527f7469616c697a65640000000000000000000000000000000000000000000000006064820152608401620016d0565b73ffffffffffffffffffffffffffffffffffffffff811662002a87576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602860248201527f416363657373436f6e74726f6c3a2061646d696e20697320746865207a65726f60448201527f20616464726573730000000000000000000000000000000000000000000000006064820152608401620016d0565b600080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b8042111562002b0d576040517f277b2d7400000000000000000000000000000000000000000000000000000000815260048101829052602401620016d0565b600c8560405162002b1f9190620048d9565b9081526040519081900360200190205460ff161562002b6e57846040517fbf42d003000000000000000000000000000000000000000000000000000000008152600401620016d0919062004943565b6001600c8660405162002b829190620048d9565b90815260408051918290036020908101832080549415157fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00909516949094179093557fc4c90a8c884e43804bc3e7d7a39f8a2f6bbfa0212985c48e240d628c228f04608383015273ffffffffffffffffffffffffffffffffffffffff87811683830152861660608301526080820185905260a08083018590528151808403909101815260c090920190528051910120600062002d37604080518082018252601481527f4d6574614c656e6420526562616c616e63696e6700000000000000000000000060209182015281518083018352600581527f332e302e300000000000000000000000000000000000000000000000000000009082015281517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f818301527f85d7f6d19ce33abcd38ffd3d658144a573976d214de6c9a3ee824fff292c8d31818401527fd7a1ce683065975771bedf401ecab037f4f4c62cc51fefdc8b39dd246ff0343a60608201524660808201523060a0808301919091528351808303909101815260c0909101909252815191012090565b6040517f1901000000000000000000000000000000000000000000000000000000000000602082015260228101919091526042810183905260620160405160208183030381529060405280519060200120905062000e0488888362003170565b600062002da785858885620025df565b62002db383856200261f565b600f5463ffffffff87811691161462002e265762002dd18662002ea2565b60065473ffffffffffffffffffffffffffffffffffffffff83811691161462002e26576040517f398d4d3200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b62002e3183620028d4565b905062000d2381836200341c565b6000806000835160411462002e865784846040517f50101a47000000000000000000000000000000000000000000000000000000008152600401620016d092919062004958565b5050506020810151604082015160609092015160001a93909250565b600f5463ffffffff8281169116148062002eec575063ffffffff811660009081526010602052604081209062002ed960046200286a565b8152602001908152602001600020546000145b1562002f2d576040517fd3616c3700000000000000000000000000000000000000000000000000000000815263ffffffff82166004820152602401620016d0565b50565b62002f3f602083018362004698565b905062002f4d838062004698565b905014158062002f7a575062002f67604083018362004698565b905062002f75838062004698565b905014155b1562002fb2576040517f7db491eb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600062002fc0838062004698565b9050905060005b818110156200313d57600062002fde858062004698565b8381811062002ff15762002ff162004119565b905060200201602081019062003008919062003f6c565b905060006200301b602087018762004698565b848181106200302e576200302e62004119565b905060200201602081019062003045919062003949565b9050600062003058604088018862004698565b858181106200306b576200306b62004119565b90506020020160208101906200308291906200421b565b905062003093602089018962003f6c565b6002811115620030a757620030a762003f8a565b836002811115620030bc57620030bc62003f8a565b148015620031065750620030d76040890160208a0162003949565b73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16145b80156200311e57508563ffffffff168163ffffffff16145b156200312e575050505050505050565b83600101935050505062002fc7565b506040517f398d4d3200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600f546040517f98ef1ed800000000000000000000000000000000000000000000000000000000815264010000000090910473ffffffffffffffffffffffffffffffffffffffff16906398ef1ed890620031d39086908590879060040162004991565b6020604051808303816000875af1158015620031f3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620032199190620049c8565b620032565782826040517f50101a47000000000000000000000000000000000000000000000000000000008152600401620016d092919062004958565b505050565b600080604051806020016200327090620035b1565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe082820381018352601f90910116604081815230602083015273ffffffffffffffffffffffffffffffffffffffff8616908201529091506000908290606001604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152908290526200330d9291602001620049e8565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152908290527fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606087901b16602083015291506000906034016040516020818303038152906040528051906020012090506200339660008284620034ce565b73ffffffffffffffffffffffffffffffffffffffff86811660008181526008602052604080822080547fffffffffffffffffffffffff000000000000000000000000000000000000000016948616948517905551939750919290917f3b2ac627f2dda3cc16038f312aeeda34474e3770c24737309735bb3911bdcf8291a3505050919050565b60075473ffffffffffffffffffffffffffffffffffffffff8084166000908152601160209081526040808320938616835292905220546200345e919062004819565b421015620034ca5773ffffffffffffffffffffffffffffffffffffffff80831660008181526011602090815260408083209486168084529490915280822082905551909291907fc78e367d3ff6f614ce683fcce3c77bc551c69a0db175f03efedbd4dd40805909908490a45b5050565b60008347101562003515576040517fe4bbecac00000000000000000000000000000000000000000000000000000000815247600482015260248101859052604401620016d0565b815160000362003551576040517f4ca249dc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8282516020840186f5905073ffffffffffffffffffffffffffffffffffffffff8116620035aa576040517f741752c200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b9392505050565b6102138062004a1c83390190565b803573ffffffffffffffffffffffffffffffffffffffff811681146200254957600080fd5b600060408284031215620035f757600080fd5b50919050565b600060a08284031215620035f757600080fd5b600080600080600060e086880312156200362957600080fd5b6200363486620035bf565b94506200364460208701620035bf565b9350620036558760408801620035e4565b9250620036668760808801620035e4565b915060c086013567ffffffffffffffff8111156200368357600080fd5b6200369188828901620035fd565b9150509295509295909350565b600060208284031215620036b157600080fd5b5035919050565b803563ffffffff811681146200254957600080fd5b8035600681106200254957600080fd5b801515811462002f2d57600080fd5b600080600080608085870312156200370357600080fd5b6200370e85620036b8565b93506200371e60208601620035bf565b92506200372e60408601620036cd565b915060608501356200374081620036dd565b939692955090935050565b8035600381106200254957600080fd5b600080600080608085870312156200377257600080fd5b6200377d85620036b8565b93506200378d60208601620035bf565b92506200379d604086016200374b565b9150620037ad60608601620035bf565b905092959194509250565b60008083601f840112620037cb57600080fd5b50813567ffffffffffffffff811115620037e457600080fd5b6020830191508360208260051b85010111156200380057600080fd5b9250929050565b6000806000604084860312156200381d57600080fd5b6200382884620035bf565b9250602084013567ffffffffffffffff8111156200384557600080fd5b6200385386828701620037b8565b9497909650939450505050565b600080604083850312156200387457600080fd5b6200387f83620035bf565b9150602083013567ffffffffffffffff8111156200389c57600080fd5b620038aa85828601620035fd565b9150509250929050565b600080600080600080600060e0888a031215620038d057600080fd5b620038db88620035bf565b9650620038eb60208901620035bf565b9550620038fb60408901620035bf565b94506200390b60608901620035bf565b93506200391b60808901620035bf565b92506200392b60a08901620036b8565b91506200393b60c08901620035bf565b905092959891949750929550565b6000602082840312156200395c57600080fd5b620035aa82620035bf565b60008083601f8401126200397a57600080fd5b50813567ffffffffffffffff8111156200399357600080fd5b6020830191508360208285010111156200380057600080fd5b60008060008060008060008060e0898b031215620039c957600080fd5b620039d489620035bf565b9750620039e460208a01620035bf565b9650620039f460408a016200374b565b955062003a0460608a01620035bf565b94506080890135935060a0890135925060c089013567ffffffffffffffff81111562003a2f57600080fd5b62003a3d8b828c0162003967565b999c989b5096995094979396929594505050565b600080600080600080600080600060a08a8c03121562003a7057600080fd5b62003a7b8a620036b8565b985060208a013567ffffffffffffffff8082111562003a9957600080fd5b62003aa78d838e01620037b8565b909a50985060408c013591508082111562003ac157600080fd5b62003acf8d838e01620037b8565b909850965060608c013591508082111562003ae957600080fd5b62003af78d838e01620037b8565b909650945060808c013591508082111562003b1157600080fd5b5062003b208c828d01620037b8565b915080935050809150509295985092959850929598565b6000806000806000806060878903121562003b5157600080fd5b863567ffffffffffffffff8082111562003b6a57600080fd5b62003b788a838b01620037b8565b9098509650602089013591508082111562003b9257600080fd5b62003ba08a838b01620037b8565b9096509450604089013591508082111562003bba57600080fd5b5062003bc989828a01620037b8565b979a9699509497509295939492505050565b600080600080600080600060c0888a03121562003bf757600080fd5b62003c0288620035bf565b965062003c138960208a01620035e4565b9550606088013567ffffffffffffffff8082111562003c3157600080fd5b62003c3f8b838c01620035fd565b965060808a013591508082111562003c5657600080fd5b62003c648b838c0162003967565b909650945060a08a013591508082111562003c7e57600080fd5b5062003c8d8a828b0162003967565b989b979a50959850939692959293505050565b600080600080600060c0868803121562003cb957600080fd5b62003cc486620035bf565b945062003cd460208701620035bf565b935062003ce460408701620036b8565b925062003cf58760608801620035e4565b915060a086013567ffffffffffffffff8111156200368357600080fd5b60008060008060008060e0878903121562003d2c57600080fd5b62003d3787620035bf565b955062003d4760208801620035bf565b945062003d5760408801620036b8565b9350606087013567ffffffffffffffff8082111562003d7557600080fd5b62003d838a838b01620035fd565b945062003d948a60808b01620035e4565b935060c089013591508082111562003dab57600080fd5b5062003dba89828a01620035fd565b9150509295509295509295565b60008060008060006080868803121562003de057600080fd5b62003deb86620035bf565b945062003dfb60208701620035bf565b935060408601359250606086013567ffffffffffffffff81111562003e1f57600080fd5b62003e2d88828901620037b8565b969995985093965092949392505050565b60008060008060008060006080888a03121562003e5a57600080fd5b62003e6588620035bf565b9650602088013567ffffffffffffffff8082111562003e8357600080fd5b62003e918b838c01620037b8565b909850965060408a013591508082111562003eab57600080fd5b62003eb98b838c01620037b8565b909650945060608a013591508082111562003ed357600080fd5b5062003c8d8a828b01620037b8565b600080600080600080610100878903121562003efd57600080fd5b62003f0887620035bf565b955062003f1860208801620036b8565b94506040870135935062003f308860608901620035e4565b925062003f418860a08901620035e4565b915060e087013567ffffffffffffffff81111562003f5e57600080fd5b62003dba89828a01620035fd565b60006020828403121562003f7f57600080fd5b620035aa826200374b565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b6003811062003fcc5762003fcc62003f8a565b9052565b73ffffffffffffffffffffffffffffffffffffffff868116825260a082019062003ffe602084018862003fb9565b808616604084015262004015606084018662003fb9565b8084166080840152509695505050505050565b6000602082840312156200403b57600080fd5b5051919050565b8183526000602080850194508260005b858110156200408f5773ffffffffffffffffffffffffffffffffffffffff6200407b83620035bf565b168752958201959082019060010162004052565b509495945050505050565b73ffffffffffffffffffffffffffffffffffffffff8416815260406020820152600062000d2360408301848662004042565b73ffffffffffffffffffffffffffffffffffffffff868116825260a0820190620040fa602084018862003fb9565b9490941660408201526060810192909252151560809091015292915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b6000602082840312156200415b57600080fd5b8135620035aa81620036dd565b63ffffffff851681526080810162004184602083018662003fb9565b73ffffffffffffffffffffffffffffffffffffffff84166040830152821515606083015295945050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203620042145762004214620041b1565b5060010190565b6000602082840312156200422e57600080fd5b620035aa82620036b8565b6000602082840312156200424c57600080fd5b620035aa82620036cd565b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b620042ac818862003fb9565b73ffffffffffffffffffffffffffffffffffffffff86166020820152608060408201526000620042e160808301868862004257565b8281036060840152620042f681858762004257565b9998505050505050505050565b63ffffffff85168152608081016200431f602083018662003fb9565b73ffffffffffffffffffffffffffffffffffffffff808516604084015280841660608401525095945050505050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126200438457600080fd5b83018035915067ffffffffffffffff821115620043a057600080fd5b6020019150368190038213156200380057600080fd5b63ffffffff8c1681526101608101620043d3602083018d62003fb9565b73ffffffffffffffffffffffffffffffffffffffff9a8b1660408301529890991660608a0152608089019690965260a088019490945260c087019290925260e086015260ff166101008501526101208401526101409092019190915292915050565b81835260007f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8311156200446857600080fd5b8260051b80836020870137939093016020019392505050565b600073ffffffffffffffffffffffffffffffffffffffff808816835280871660208401525084604083015260806060830152620044c360808301848662004435565b979650505050505050565b8082028115828204841417620021d657620021d6620041b1565b6000826200451f577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b73ffffffffffffffffffffffffffffffffffffffff88168152600060206080818401526200455760808401898b62004042565b83810360408501526200456c81888a62004435565b84810360608601528581529050818101600586811b830184018860005b898110156200463d577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe086840301855281357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18c3603018112620045ec57600080fd5b8b01878101903567ffffffffffffffff8111156200460957600080fd5b80861b36038213156200461b57600080fd5b6200462885828462004435565b96890196945050509086019060010162004589565b50909e9d5050505050505050505050505050565b8481526080810162004667602083018662003fb9565b73ffffffffffffffffffffffffffffffffffffffff8416604083015263ffffffff8316606083015295945050505050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112620046ce57600080fd5b83018035915067ffffffffffffffff821115620046ea57600080fd5b6020019150600581901b36038213156200380057600080fd5b8183526000602080850194508260005b858110156200408f5762004732876200472c846200374b565b62003fb9565b958201959082019060010162004713565b8183526000602080850194508260005b858110156200408f5763ffffffff6200476c83620036b8565b168752958201959082019060010162004753565b6080815260006200479660808301898b62004703565b8281036020840152620047ab81888a62004042565b90508281036040840152620047c281868862004743565b91505082606083015298975050505050505050565b606081526000620047ed60608301888a62004703565b82810360208401526200480281878962004042565b90508281036040840152620042f681858762004743565b80820180821115620021d657620021d6620041b1565b60006006831062004844576200484462003f8a565b5060f89190911b815260010190565b60007fffffffffffffffffffffffffffffffffffffffff000000000000000000000000808660601b1683526003851062004891576200489162003f8a565b60f89490941b60148301525060609190911b9091166015820152602901919050565b60005b83811015620048d0578181015183820152602001620048b6565b50506000910152565b60008251620048ed818460208701620048b3565b9190910192915050565b6000815180845262004911816020860160208601620048b3565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b602081526000620035aa6020830184620048f7565b73ffffffffffffffffffffffffffffffffffffffff83168152604060208201526000620049896040830184620048f7565b949350505050565b73ffffffffffffffffffffffffffffffffffffffff8416815282602082015260606040820152600062000d236060830184620048f7565b600060208284031215620049db57600080fd5b8151620035aa81620036dd565b60008351620049fc818460208801620048b3565b83519083019062004a12818360208801620048b3565b0194935050505056fe608060405234801561001057600080fd5b5060405161021338038061021383398101604081905261002f916100ac565b600080546001600160a01b038085166001600160a01b03199283168117845560018054928616929093168217909255604051909230917f5de57f6bdaa5661f65214dfcfe60688ade6759f45b42d2db77b8139e0571e9089190a450506100df565b80516001600160a01b03811681146100a757600080fd5b919050565b600080604083850312156100bf57600080fd5b6100c883610090565b91506100d660208401610090565b90509250929050565b610125806100ee6000396000f3fe608060405261000c61000e565b005b61001e610019610020565b6100b7565b565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663a9fda0eb6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561008e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906100b291906100db565b905090565b3660008037600080366000845af43d6000803e8080156100d6573d6000f35b3d6000fd5b6000602082840312156100ed57600080fd5b815173ffffffffffffffffffffffffffffffffffffffff8116811461011157600080fd5b939250505056fea164736f6c6343000814000aa164736f6c6343000814000a
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
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.