Feature Tip: Add private address tag to any address under My Name Tag !
Latest 25 from a total of 72,690 transactions
| Transaction Hash |
Method
|
Block
|
From
|
|
To
|
||||
|---|---|---|---|---|---|---|---|---|---|
| Propose Root Bun... | 24511001 | 8 mins ago | IN | 0 ETH | 0.0000046 | ||||
| Propose Root Bun... | 24510843 | 40 mins ago | IN | 0 ETH | 0.00000354 | ||||
| Propose Root Bun... | 24510685 | 1 hr ago | IN | 0 ETH | 0.00000317 | ||||
| Propose Root Bun... | 24510527 | 1 hr ago | IN | 0 ETH | 0.00000336 | ||||
| Remove Liquidity | 24510415 | 2 hrs ago | IN | 0 ETH | 0.00000468 | ||||
| Propose Root Bun... | 24510371 | 2 hrs ago | IN | 0 ETH | 0.00000315 | ||||
| Propose Root Bun... | 24510212 | 2 hrs ago | IN | 0 ETH | 0.00000355 | ||||
| Propose Root Bun... | 24510054 | 3 hrs ago | IN | 0 ETH | 0.00000365 | ||||
| Propose Root Bun... | 24509898 | 3 hrs ago | IN | 0 ETH | 0.00000359 | ||||
| Propose Root Bun... | 24509740 | 4 hrs ago | IN | 0 ETH | 0.00000347 | ||||
| Propose Root Bun... | 24509585 | 4 hrs ago | IN | 0 ETH | 0.00000333 | ||||
| Propose Root Bun... | 24509427 | 5 hrs ago | IN | 0 ETH | 0.00000342 | ||||
| Propose Root Bun... | 24509270 | 5 hrs ago | IN | 0 ETH | 0.00000352 | ||||
| Remove Liquidity | 24509214 | 6 hrs ago | IN | 0 ETH | 0.00000323 | ||||
| Remove Liquidity | 24509144 | 6 hrs ago | IN | 0 ETH | 0.0000032 | ||||
| Propose Root Bun... | 24509116 | 6 hrs ago | IN | 0 ETH | 0.00000343 | ||||
| Propose Root Bun... | 24508955 | 6 hrs ago | IN | 0 ETH | 0.00000329 | ||||
| Propose Root Bun... | 24508796 | 7 hrs ago | IN | 0 ETH | 0.0000034 | ||||
| Propose Root Bun... | 24508632 | 8 hrs ago | IN | 0 ETH | 0.00000362 | ||||
| Propose Root Bun... | 24508473 | 8 hrs ago | IN | 0 ETH | 0.00000489 | ||||
| Propose Root Bun... | 24508313 | 9 hrs ago | IN | 0 ETH | 0.00000516 | ||||
| Propose Root Bun... | 24508155 | 9 hrs ago | IN | 0 ETH | 0.00000738 | ||||
| Propose Root Bun... | 24507999 | 10 hrs ago | IN | 0 ETH | 0.00000433 | ||||
| Propose Root Bun... | 24507843 | 10 hrs ago | IN | 0 ETH | 0.00000343 | ||||
| Propose Root Bun... | 24507682 | 11 hrs ago | IN | 0 ETH | 0.00000326 |
Latest 25 internal transactions (View All)
Advanced mode:
| Parent Transaction Hash | Method | Block |
From
|
|
To
|
||
|---|---|---|---|---|---|---|---|
| Send Message | 24510996 | 9 mins ago | 0.00000024 ETH | ||||
| Create Retryable... | 24510996 | 9 mins ago | 0.02 ETH | ||||
| Send Message | 24510841 | 41 mins ago | 0.00000024 ETH | ||||
| Create Retryable... | 24510841 | 41 mins ago | 0.02 ETH | ||||
| Send Message | 24510683 | 1 hr ago | 0.00000024 ETH | ||||
| Create Retryable... | 24510683 | 1 hr ago | 0.02 ETH | ||||
| Request L2Transa... | 24510683 | 1 hr ago | 0.00050076 ETH | ||||
| Send Message | 24510526 | 1 hr ago | 0.00000024 ETH | ||||
| Create Retryable... | 24510526 | 1 hr ago | 0.02 ETH | ||||
| Request L2Transa... | 24510526 | 1 hr ago | 0.00050078 ETH | ||||
| Send Message | 24510369 | 2 hrs ago | 0.00000024 ETH | ||||
| Create Retryable... | 24510369 | 2 hrs ago | 0.02 ETH | ||||
| Send | 24510369 | 2 hrs ago | 0.00001089 ETH | ||||
| Send Message | 24510211 | 2 hrs ago | 0.00000024 ETH | ||||
| Deposit ETH To | 24510211 | 2 hrs ago | 0.0007644 ETH | ||||
| Transfer | 24510211 | 2 hrs ago | 0.0007644 ETH | ||||
| Create Retryable... | 24510211 | 2 hrs ago | 0.02 ETH | ||||
| Request L2Transa... | 24510211 | 2 hrs ago | 0.00050075 ETH | ||||
| Send Message | 24510053 | 3 hrs ago | 0.00000024 ETH | ||||
| Create Retryable... | 24510053 | 3 hrs ago | 0.02 ETH | ||||
| Request L2Transa... | 24510053 | 3 hrs ago | 0.00050082 ETH | ||||
| Send Message | 24509897 | 3 hrs ago | 0.00000024 ETH | ||||
| Create Retryable... | 24509897 | 3 hrs ago | 0.02 ETH | ||||
| Send | 24509897 | 3 hrs ago | 0.00004686 ETH | ||||
| Request L2Transa... | 24509897 | 3 hrs ago | 0.00050076 ETH |
Loading...
Loading
Loading...
Loading
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:
HubPool
Compiler Version
v0.8.13+commit.abaa5c0e
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity ^0.8.0;
import "./MerkleLib.sol";
import "./HubPoolInterface.sol";
import "./Lockable.sol";
import "./interfaces/LpTokenFactoryInterface.sol";
import "./interfaces/WETH9.sol";
import "@uma/core/contracts/common/implementation/Testable.sol";
import "@uma/core/contracts/common/implementation/MultiCaller.sol";
import "@uma/core/contracts/oracle/implementation/Constants.sol";
import "@uma/core/contracts/common/interfaces/AddressWhitelistInterface.sol";
import "@uma/core/contracts/oracle/interfaces/IdentifierWhitelistInterface.sol";
import "@uma/core/contracts/oracle/interfaces/FinderInterface.sol";
import "@uma/core/contracts/oracle/interfaces/StoreInterface.sol";
import "@uma/core/contracts/oracle/interfaces/SkinnyOptimisticOracleInterface.sol";
import "@uma/core/contracts/common/interfaces/ExpandedIERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/utils/Address.sol";
/**
* @notice Contract deployed on Ethereum that houses L1 token liquidity for all SpokePools. A dataworker can interact
* with merkle roots stored in this contract via inclusion proofs to instruct this contract to send tokens to L2
* SpokePools via "pool rebalances" that can be used to pay out relayers on those networks. This contract is also
* responsible for publishing relayer refund and slow relay merkle roots to SpokePools.
* @notice This contract is meant to act as the cross chain administrator and owner of all L2 spoke pools, so all
* governance actions and pool rebalances originate from here and bridge instructions to L2s.
* @dev This contract should be deprecated by the year 2106, at which point uint32 timestamps will roll over. This is
* an issue for this contract because fee calculations will become bizarre when multiplying by negative time deltas.
* Before this date, this contract should be paused from accepting new root bundles and all LP tokens should be
* disabled by the admin.
*/
contract HubPool is HubPoolInterface, Testable, Lockable, MultiCaller, Ownable {
using SafeERC20 for IERC20;
using Address for address;
// Only one root bundle can be stored at a time. Once all pool rebalance leaves are executed, a new proposal
// can be submitted.
RootBundle public rootBundleProposal;
// Mapping of L1 token addresses to the associated pool information.
mapping(address => PooledToken) public pooledTokens;
// Stores paths from L1 token + destination ID to destination token. Since different tokens on L1 might map to
// to the same address on different destinations, we hash (L1 token address, destination ID) to
// use as a key that maps to a destination token. This mapping is used to direct pool rebalances from
// HubPool to SpokePool, and also is designed to be used as a lookup for off-chain data workers to determine
// which L1 tokens to relay to SpokePools to refund relayers. The admin can set the "destination token"
// to 0x0 to disable a pool rebalance route and block executeRootBundle() from executing.
mapping(bytes32 => address) private poolRebalanceRoutes;
// Mapping of chainId to the associated adapter and spokePool contracts.
mapping(uint256 => CrossChainContract) public crossChainContracts;
mapping(address => uint256) public unclaimedAccumulatedProtocolFees;
// Whether the bundle proposal process is paused.
bool public paused;
// WETH contract for Ethereum.
WETH9 public immutable weth;
// Helper factory to deploy new LP tokens for enabled L1 tokens
LpTokenFactoryInterface public immutable lpTokenFactory;
// Finder contract for this network.
FinderInterface public immutable finder;
// Address that captures protocol fees. Accumulated protocol fees can be claimed by this address.
address public protocolFeeCaptureAddress;
// Token used to bond the data worker for proposing relayer refund bundles.
IERC20 public bondToken;
// Each root bundle proposal must stay in liveness for this period of time before it can be considered finalized.
// It can be disputed only during this period of time. Defaults to 2 hours, like the rest of the UMA ecosystem.
uint32 public liveness = 7200;
// When root bundles are disputed a price request is enqueued with the DVM to resolve the resolution.
bytes32 public identifier = "IS_ACROSS_V2_BUNDLE_VALID";
// Interest rate payment that scales the amount of pending fees per second paid to LPs. 0.0000015e18 will pay out
// the full amount of fees entitled to LPs in ~ 7.72 days assuming no contract interactions. If someone interacts
// with the contract then the LP rewards are smeared sublinearly over the window (i.e spread over the remaining
// period for each interaction which approximates a decreasing exponential function).
uint256 public lpFeeRatePerSecond = 1500000000000;
// Percentage of lpFees that are captured by the protocol and claimable by the protocolFeeCaptureAddress.
uint256 public protocolFeeCapturePct;
// The computed bond amount as the UMA Store's final fee multiplied by the bondTokenFinalFeeMultiplier.
uint256 public bondAmount;
event Paused(bool indexed isPaused);
event EmergencyRootBundleDeleted(
bytes32 indexed poolRebalanceRoot,
bytes32 indexed relayerRefundRoot,
bytes32 slowRelayRoot,
address indexed proposer
);
event ProtocolFeeCaptureSet(address indexed newProtocolFeeCaptureAddress, uint256 indexed newProtocolFeeCapturePct);
event ProtocolFeesCapturedClaimed(address indexed l1Token, uint256 indexed accumulatedFees);
event BondSet(address indexed newBondToken, uint256 newBondAmount);
event LivenessSet(uint256 newLiveness);
event IdentifierSet(bytes32 newIdentifier);
event CrossChainContractsSet(uint256 l2ChainId, address adapter, address spokePool);
event L1TokenEnabledForLiquidityProvision(address l1Token, address lpToken);
event L2TokenDisabledForLiquidityProvision(address l1Token, address lpToken);
event LiquidityAdded(
address indexed l1Token,
uint256 amount,
uint256 lpTokensMinted,
address indexed liquidityProvider
);
event LiquidityRemoved(
address indexed l1Token,
uint256 amount,
uint256 lpTokensBurnt,
address indexed liquidityProvider
);
event SetPoolRebalanceRoute(
uint256 indexed destinationChainId,
address indexed l1Token,
address indexed destinationToken
);
event SetEnableDepositRoute(
uint256 indexed originChainId,
uint256 indexed destinationChainId,
address indexed originToken,
bool depositsEnabled
);
event ProposeRootBundle(
uint32 challengePeriodEndTimestamp,
uint8 poolRebalanceLeafCount,
uint256[] bundleEvaluationBlockNumbers,
bytes32 indexed poolRebalanceRoot,
bytes32 indexed relayerRefundRoot,
bytes32 slowRelayRoot,
address indexed proposer
);
event RootBundleExecuted(
uint256 groupIndex,
uint256 indexed leafId,
uint256 indexed chainId,
address[] l1Tokens,
uint256[] bundleLpFees,
int256[] netSendAmounts,
int256[] runningBalances,
address indexed caller
);
event SpokePoolAdminFunctionTriggered(uint256 indexed chainId, bytes message);
event RootBundleDisputed(address indexed disputer, uint256 requestTime);
event RootBundleCanceled(address indexed disputer, uint256 requestTime);
modifier noActiveRequests() {
require(!_activeRequest(), "Proposal has unclaimed leaves");
_;
}
modifier unpaused() {
require(!paused, "Contract is paused");
_;
}
modifier zeroOptimisticOracleApproval() {
_;
bondToken.safeApprove(address(_getOptimisticOracle()), 0);
}
/**
* @notice Construct HubPool.
* @param _lpTokenFactory LP Token factory address used to deploy LP tokens for new collateral types.
* @param _finder Finder address.
* @param _weth WETH address.
* @param _timer Timer address.
*/
constructor(
LpTokenFactoryInterface _lpTokenFactory,
FinderInterface _finder,
WETH9 _weth,
address _timer
) Testable(_timer) {
lpTokenFactory = _lpTokenFactory;
finder = _finder;
weth = _weth;
protocolFeeCaptureAddress = owner();
}
/*************************************************
* ADMIN FUNCTIONS *
*************************************************/
/**
* @notice Pauses the bundle proposal and execution process. This is intended to be used during upgrades or when
* something goes awry.
* @param pause true if the call is meant to pause the system, false if the call is meant to unpause it.
*/
function setPaused(bool pause) public onlyOwner nonReentrant {
paused = pause;
emit Paused(pause);
}
/**
* @notice This allows for the deletion of the active proposal in case of emergency.
* @dev This is primarily intended to rectify situations where an unexecutable bundle gets through liveness in the
* case of a non-malicious bug in the proposal/dispute code. Without this function, the contract would be
* indefinitely blocked, migration would be required, and in-progress transfers would never be repaid.
*/
function emergencyDeleteProposal() public onlyOwner nonReentrant {
RootBundle memory _rootBundleProposal = rootBundleProposal;
delete rootBundleProposal;
if (_rootBundleProposal.unclaimedPoolRebalanceLeafCount > 0)
bondToken.safeTransfer(_rootBundleProposal.proposer, bondAmount);
emit EmergencyRootBundleDeleted(
_rootBundleProposal.poolRebalanceRoot,
_rootBundleProposal.relayerRefundRoot,
_rootBundleProposal.slowRelayRoot,
_rootBundleProposal.proposer
);
}
/**
* @notice Sends message to SpokePool from this contract. Callable only by owner.
* @dev This function has permission to call onlyAdmin functions on the SpokePool, so it's imperative that this
* contract only allows the owner to call this method directly or indirectly.
* @param chainId Chain with SpokePool to send message to.
* @param functionData ABI encoded function call to send to SpokePool, but can be any arbitrary data technically.
*/
function relaySpokePoolAdminFunction(uint256 chainId, bytes memory functionData)
public
override
onlyOwner
nonReentrant
{
_relaySpokePoolAdminFunction(chainId, functionData);
}
/**
* @notice Sets protocolFeeCaptureAddress and protocolFeeCapturePct. Callable only by owner.
* @param newProtocolFeeCaptureAddress New protocol fee capture address.
* @param newProtocolFeeCapturePct New protocol fee capture %.
*/
function setProtocolFeeCapture(address newProtocolFeeCaptureAddress, uint256 newProtocolFeeCapturePct)
public
override
onlyOwner
nonReentrant
{
require(newProtocolFeeCapturePct <= 1e18, "Bad protocolFeeCapturePct");
require(newProtocolFeeCaptureAddress != address(0), "Bad protocolFeeCaptureAddress");
protocolFeeCaptureAddress = newProtocolFeeCaptureAddress;
protocolFeeCapturePct = newProtocolFeeCapturePct;
emit ProtocolFeeCaptureSet(newProtocolFeeCaptureAddress, newProtocolFeeCapturePct);
}
/**
* @notice Sets bond token and amount. Callable only by owner.
* @param newBondToken New bond currency.
* @param newBondAmount New bond amount.
*/
function setBond(IERC20 newBondToken, uint256 newBondAmount)
public
override
onlyOwner
noActiveRequests
nonReentrant
{
// Bond should not equal final fee otherwise every proposal will get cancelled in a dispute.
// In practice we expect that bond amounts are set >> final fees so this shouldn't be an inconvenience.
// The only way for the bond amount to be equal to the final fee is if the newBondAmount == 0.
require(newBondAmount != 0, "bond equal to final fee");
// Check that this token is on the whitelist.
AddressWhitelistInterface addressWhitelist = AddressWhitelistInterface(
finder.getImplementationAddress(OracleInterfaces.CollateralWhitelist)
);
require(addressWhitelist.isOnWhitelist(address(newBondToken)), "Not on whitelist");
// The bond should be the passed in bondAmount + the final fee.
bondToken = newBondToken;
uint256 _bondAmount = newBondAmount + _getBondTokenFinalFee();
bondAmount = _bondAmount;
emit BondSet(address(newBondToken), _bondAmount);
}
/**
* @notice Sets root bundle proposal liveness period. Callable only by owner.
* @param newLiveness New liveness period.
*/
function setLiveness(uint32 newLiveness) public override onlyOwner nonReentrant {
require(newLiveness > 10 minutes, "Liveness too short");
liveness = newLiveness;
emit LivenessSet(newLiveness);
}
/**
* @notice Sets identifier for root bundle disputes. Callable only by owner.
* @param newIdentifier New identifier.
*/
function setIdentifier(bytes32 newIdentifier) public override onlyOwner noActiveRequests nonReentrant {
IdentifierWhitelistInterface identifierWhitelist = IdentifierWhitelistInterface(
finder.getImplementationAddress(OracleInterfaces.IdentifierWhitelist)
);
require(identifierWhitelist.isIdentifierSupported(newIdentifier), "Identifier not supported");
identifier = newIdentifier;
emit IdentifierSet(newIdentifier);
}
/**
* @notice Sets cross chain relay helper contracts for L2 chain ID. Callable only by owner.
* @dev We do not block setting the adapter or SpokePool to invalid/zero addresses because we want to allow the
* admin to block relaying roots to the spoke pool for emergency recovery purposes.
* @param l2ChainId Chain to set contracts for.
* @param adapter Adapter used to relay messages and tokens to spoke pool. Deployed on current chain.
* @param spokePool Recipient of relayed messages and tokens on spoke pool. Deployed on l2ChainId.
*/
function setCrossChainContracts(
uint256 l2ChainId,
address adapter,
address spokePool
) public override onlyOwner nonReentrant {
crossChainContracts[l2ChainId] = CrossChainContract(adapter, spokePool);
emit CrossChainContractsSet(l2ChainId, adapter, spokePool);
}
/**
* @notice Store canonical destination token counterpart for l1 token. Callable only by owner.
* @dev Admin can set destinationToken to 0x0 to effectively disable executing any root bundles with leaves
* containing this l1 token + destination chain ID combination.
* @param destinationChainId Destination chain where destination token resides.
* @param l1Token Token enabled for liquidity in this pool, and the L1 counterpart to the destination token on the
* destination chain ID.
* @param destinationToken Destination chain counterpart of L1 token.
*/
function setPoolRebalanceRoute(
uint256 destinationChainId,
address l1Token,
address destinationToken
) public override onlyOwner nonReentrant {
poolRebalanceRoutes[_poolRebalanceRouteKey(l1Token, destinationChainId)] = destinationToken;
emit SetPoolRebalanceRoute(destinationChainId, l1Token, destinationToken);
}
/**
* @notice Sends cross-chain message to SpokePool on originChainId to enable or disable deposit route from that
* SpokePool to another one. Callable only by owner.
* @dev Admin is responsible for ensuring that `originToken` is linked to some L1 token on this contract, via
* poolRebalanceRoutes(), and that this L1 token also has a counterpart on the destination chain. If either
* condition fails, then the deposit will be unrelayable by off-chain relayers because they will not know which
* token to relay to recipients on the destination chain, and data workers wouldn't know which L1 token to send
* to the destination chain to refund the relayer.
* @param originChainId Chain where token deposit occurs.
* @param destinationChainId Chain where token depositor wants to receive funds.
* @param originToken Token sent in deposit.
* @param depositsEnabled Set to true to whitelist this route for deposits, set to false if caller just wants to
* map the origin token + destination ID to the destination token address on the origin chain's SpokePool.
*/
function setDepositRoute(
uint256 originChainId,
uint256 destinationChainId,
address originToken,
bool depositsEnabled
) public override nonReentrant onlyOwner {
_relaySpokePoolAdminFunction(
originChainId,
abi.encodeWithSignature(
"setEnableRoute(address,uint256,bool)",
originToken,
destinationChainId,
depositsEnabled
)
);
emit SetEnableDepositRoute(originChainId, destinationChainId, originToken, depositsEnabled);
}
/**
* @notice Enables LPs to provide liquidity for L1 token. Deploys new LP token for L1 token if appropriate.
* Callable only by owner.
* @param l1Token Token to provide liquidity for.
*/
function enableL1TokenForLiquidityProvision(address l1Token) public override onlyOwner nonReentrant {
// If token is being enabled for the first time, create a new LP token and set the timestamp once. We don't
// want to ever reset this timestamp otherwise fees that have accrued will be lost since the last update. This
// could happen for example if an L1 token is enabled, disabled, and then enabled again.
if (pooledTokens[l1Token].lpToken == address(0)) {
pooledTokens[l1Token].lpToken = lpTokenFactory.createLpToken(l1Token);
pooledTokens[l1Token].lastLpFeeUpdate = uint32(getCurrentTime());
}
pooledTokens[l1Token].isEnabled = true;
emit L1TokenEnabledForLiquidityProvision(l1Token, pooledTokens[l1Token].lpToken);
}
/**
* @notice Disables LPs from providing liquidity for L1 token. Callable only by owner.
* @param l1Token Token to disable liquidity provision for.
*/
function disableL1TokenForLiquidityProvision(address l1Token) public override onlyOwner nonReentrant {
pooledTokens[l1Token].isEnabled = false;
emit L2TokenDisabledForLiquidityProvision(l1Token, pooledTokens[l1Token].lpToken);
}
/**
* @notice Enables the owner of the protocol to haircut reserves in the event of an irrecoverable loss of funds on
* one of the L2s. Consider funds are leant out onto a L2 that dies irrecoverably. This value will offset the
* exchangeRateCurrent such that all LPs receive a pro rata loss of the the reserves. Should be used in conjunction
* with pause logic to prevent LPs from adding/withdrawing liquidity during the haircut process.
* Callable only by owner.
* @param l1Token Token to execute the haircut on.
* @param haircutAmount The amount of reserves to haircut the LPs by.
*/
function haircutReserves(address l1Token, int256 haircutAmount) public onlyOwner nonReentrant {
// Note that we do not call sync first in this method. The Owner should call this manually before haircutting.
// This is done in the event sync is reverting due to too low balanced in the contract relative to bond amount.
pooledTokens[l1Token].utilizedReserves -= haircutAmount;
}
/*************************************************
* LIQUIDITY PROVIDER FUNCTIONS *
*************************************************/
/**
* @notice Deposit liquidity into this contract to earn LP fees in exchange for funding relays on SpokePools.
* Caller is essentially loaning their funds to be sent from this contract to the SpokePool, where it will be used
* to repay a relayer, and ultimately receives their loan back after the tokens are bridged back to this contract
* via the canonical token bridge. Then, the caller's loans are used again. This loan cycle repeats continuously
* and the caller, or "liquidity provider" earns a continuous fee for their credit that they are extending relayers.
* @notice Caller will receive an LP token representing their share of this pool. The LP token's redemption value
* increments from the time that they enter the pool to reflect their accrued fees.
* @notice The caller of this function must approve this contract to spend l1TokenAmount of l1Token.
* @param l1Token Token to deposit into this contract.
* @param l1TokenAmount Amount of liquidity to provide.
*/
function addLiquidity(address l1Token, uint256 l1TokenAmount) public payable override nonReentrant unpaused {
require(pooledTokens[l1Token].isEnabled, "Token not enabled");
// If this is the weth pool and the caller sends msg.value then the msg.value must match the l1TokenAmount.
// Else, msg.value must be set to 0.
require(((address(weth) == l1Token) && msg.value == l1TokenAmount) || msg.value == 0, "Bad msg.value");
// Since _exchangeRateCurrent() reads this contract's balance and updates contract state using it, it must be
// first before transferring any tokens to this contract to ensure synchronization.
uint256 lpTokensToMint = (l1TokenAmount * 1e18) / _exchangeRateCurrent(l1Token);
pooledTokens[l1Token].liquidReserves += l1TokenAmount;
ExpandedIERC20(pooledTokens[l1Token].lpToken).mint(msg.sender, lpTokensToMint);
if (address(weth) == l1Token && msg.value > 0) WETH9(address(l1Token)).deposit{ value: msg.value }();
else IERC20(l1Token).safeTransferFrom(msg.sender, address(this), l1TokenAmount);
emit LiquidityAdded(l1Token, l1TokenAmount, lpTokensToMint, msg.sender);
}
/**
* @notice Burns LP share to redeem for underlying l1Token original deposit amount plus fees.
* @param l1Token Token to redeem LP share for.
* @param lpTokenAmount Amount of LP tokens to burn. Exchange rate between L1 token and LP token can be queried
* via public exchangeRateCurrent method.
* @param sendEth Set to True if L1 token is WETH and user wants to receive ETH. Note that if caller
* is a contract, then the contract should have a way to receive ETH if this value is set to True. Similarly,
* if this value is set to False, then the calling contract should have a way to handle WETH.
*/
function removeLiquidity(
address l1Token,
uint256 lpTokenAmount,
bool sendEth
) public override nonReentrant unpaused {
require(address(weth) == l1Token || !sendEth, "Cant send eth");
uint256 l1TokensToReturn = (lpTokenAmount * _exchangeRateCurrent(l1Token)) / 1e18;
ExpandedIERC20(pooledTokens[l1Token].lpToken).burnFrom(msg.sender, lpTokenAmount);
// Note this method does not make any liquidity utilization checks before letting the LP redeem their LP tokens.
// If they try access more funds than available (i.e l1TokensToReturn > liquidReserves) this will underflow.
pooledTokens[l1Token].liquidReserves -= l1TokensToReturn;
if (sendEth) {
weth.withdraw(l1TokensToReturn);
Address.sendValue(payable(msg.sender), l1TokensToReturn); // This will revert if the caller is a contract that does not implement a fallback function.
} else {
IERC20(address(l1Token)).safeTransfer(msg.sender, l1TokensToReturn);
}
emit LiquidityRemoved(l1Token, l1TokensToReturn, lpTokenAmount, msg.sender);
}
/**
* @notice Returns exchange rate of L1 token to LP token.
* @param l1Token L1 token redeemable by burning LP token.
* @return Amount of L1 tokens redeemable for 1 unit LP token.
*/
function exchangeRateCurrent(address l1Token) public override nonReentrant returns (uint256) {
return _exchangeRateCurrent(l1Token);
}
/**
* @notice Returns % of liquid reserves currently being "used" and sitting in SpokePools.
* @param l1Token L1 token to query utilization for.
* @return % of liquid reserves currently being "used" and sitting in SpokePools.
*/
function liquidityUtilizationCurrent(address l1Token) public override nonReentrant returns (uint256) {
return _liquidityUtilizationPostRelay(l1Token, 0);
}
/**
* @notice Returns % of liquid reserves currently being "used" and sitting in SpokePools and accounting for
* relayedAmount of tokens to be withdrawn from the pool.
* @param l1Token L1 token to query utilization for.
* @param relayedAmount The higher this amount, the higher the utilization.
* @return % of liquid reserves currently being "used" and sitting in SpokePools plus the relayedAmount.
*/
function liquidityUtilizationPostRelay(address l1Token, uint256 relayedAmount)
public
nonReentrant
returns (uint256)
{
return _liquidityUtilizationPostRelay(l1Token, relayedAmount);
}
/**
* @notice Synchronize any balance changes in this contract with the utilized & liquid reserves. This should be done
* at the conclusion of a L2->L1 token transfer via the canonical token bridge, when this contract's reserves do not
* reflect its true balance due to new tokens being dropped onto the contract at the conclusion of a bridging action.
*/
function sync(address l1Token) public override nonReentrant {
_sync(l1Token);
}
/*************************************************
* DATA WORKER FUNCTIONS *
*************************************************/
/**
* @notice Publish a new root bundle along with all of the block numbers that the merkle roots are relevant for.
* This is used to aid off-chain validators in evaluating the correctness of this bundle. Caller stakes a bond that
* can be slashed if the root bundle proposal is invalid, and they will receive it back if accepted.
* @notice After proposeRootBundle is called, if the any props are wrong then this proposal can be challenged.
* Once the challenge period passes, then the roots are no longer disputable, and only executeRootBundle can be
* called; moreover, this method can't be called again until all leaves are executed.
* @param bundleEvaluationBlockNumbers should contain the latest block number for all chains, even if there are no
* relays contained on some of them. The usage of this variable should be defined in an off chain UMIP.
* @notice The caller of this function must approve this contract to spend bondAmount of bondToken.
* @param poolRebalanceLeafCount Number of leaves contained in pool rebalance root. Max is # of whitelisted chains.
* @param poolRebalanceRoot Pool rebalance root containing leaves that sends tokens from this contract to SpokePool.
* @param relayerRefundRoot Relayer refund root to publish to SpokePool where a data worker can execute leaves to
* refund relayers on their chosen refund chainId.
* @param slowRelayRoot Slow relay root to publish to Spoke Pool where a data worker can execute leaves to
* fulfill slow relays.
*/
function proposeRootBundle(
uint256[] calldata bundleEvaluationBlockNumbers,
uint8 poolRebalanceLeafCount,
bytes32 poolRebalanceRoot,
bytes32 relayerRefundRoot,
bytes32 slowRelayRoot
) public override nonReentrant noActiveRequests unpaused {
// Note: this is to prevent "empty block" style attacks where someone can make empty proposals that are
// technically valid but not useful. This could also potentially be enforced at the UMIP-level.
require(poolRebalanceLeafCount > 0, "Bundle must have at least 1 leaf");
uint32 challengePeriodEndTimestamp = uint32(getCurrentTime()) + liveness;
delete rootBundleProposal; // Only one bundle of roots can be executed at a time. Delete the previous bundle.
rootBundleProposal.challengePeriodEndTimestamp = challengePeriodEndTimestamp;
rootBundleProposal.unclaimedPoolRebalanceLeafCount = poolRebalanceLeafCount;
rootBundleProposal.poolRebalanceRoot = poolRebalanceRoot;
rootBundleProposal.relayerRefundRoot = relayerRefundRoot;
rootBundleProposal.slowRelayRoot = slowRelayRoot;
rootBundleProposal.proposer = msg.sender;
// Pull bondAmount of bondToken from the caller.
bondToken.safeTransferFrom(msg.sender, address(this), bondAmount);
emit ProposeRootBundle(
challengePeriodEndTimestamp,
poolRebalanceLeafCount,
bundleEvaluationBlockNumbers,
poolRebalanceRoot,
relayerRefundRoot,
slowRelayRoot,
msg.sender
);
}
/**
* @notice Executes a pool rebalance leaf as part of the currently published root bundle. Will bridge any tokens
* from this contract to the SpokePool designated in the leaf, and will also publish relayer refund and slow
* relay roots to the SpokePool on the network specified in the leaf.
* @dev In some cases, will instruct spokePool to send funds back to L1.
* @param chainId ChainId number of the target spoke pool on which the bundle is executed.
* @param groupIndex If set to 0, then relay roots to SpokePool via cross chain bridge. Used by off-chain validator
* to organize leaves with the same chain ID and also set which leaves should result in relayed messages.
* @param bundleLpFees Array representing the total LP fee amount per token in this bundle for all bundled relays.
* @param netSendAmounts Array representing the amount of tokens to send to the SpokePool on the target chainId.
* @param runningBalances Array used to track any unsent tokens that are not included in the netSendAmounts.
* @param leafId Index of this executed leaf within the poolRebalance tree.
* @param l1Tokens Array of all the tokens associated with the bundleLpFees, nedSendAmounts and runningBalances.
* @param proof Inclusion proof for this leaf in pool rebalance root in root bundle.
*/
function executeRootBundle(
uint256 chainId,
uint256 groupIndex,
uint256[] memory bundleLpFees,
int256[] memory netSendAmounts,
int256[] memory runningBalances,
uint8 leafId,
address[] memory l1Tokens,
bytes32[] calldata proof
) public nonReentrant unpaused {
require(getCurrentTime() > rootBundleProposal.challengePeriodEndTimestamp, "Not passed liveness");
// Verify the leafId in the poolRebalanceLeaf has not yet been claimed.
require(!MerkleLib.isClaimed1D(rootBundleProposal.claimedBitMap, leafId), "Already claimed");
// Verify the props provided generate a leaf that, along with the proof, are included in the merkle root.
require(
MerkleLib.verifyPoolRebalance(
rootBundleProposal.poolRebalanceRoot,
PoolRebalanceLeaf({
chainId: chainId,
groupIndex: groupIndex,
bundleLpFees: bundleLpFees,
netSendAmounts: netSendAmounts,
runningBalances: runningBalances,
leafId: leafId,
l1Tokens: l1Tokens
}),
proof
),
"Bad Proof"
);
// Grouping code that uses adapter and spokepool to avoid stack too deep warning.
// Get cross chain helpers for leaf's destination chain ID. This internal method will revert if either helper
// is set improperly.
(address adapter, address spokePool) = _getInitializedCrossChainContracts(chainId);
// Set the leafId in the claimed bitmap.
rootBundleProposal.claimedBitMap = MerkleLib.setClaimed1D(rootBundleProposal.claimedBitMap, leafId);
// Decrement the unclaimedPoolRebalanceLeafCount.
--rootBundleProposal.unclaimedPoolRebalanceLeafCount;
// Relay each L1 token to destination chain.
// Note: if any of the keccak256(l1Tokens, chainId) combinations are not mapped to a destination token address,
// then this internal method will revert. In this case the admin will have to associate a destination token
// with each l1 token. If the destination token mapping was missing at the time of the proposal, we assume
// that the root bundle would have been disputed because the off-chain data worker would have been unable to
// determine if the relayers used the correct destination token for a given origin token.
_sendTokensToChainAndUpdatePooledTokenTrackers(
adapter,
spokePool,
chainId,
l1Tokens,
netSendAmounts,
bundleLpFees
);
// Check bool used by data worker to prevent relaying redundant roots to SpokePool.
if (groupIndex == 0) {
// Relay root bundles to spoke pool on destination chain by
// performing delegatecall to use the adapter's code with this contract's context.
// We are ok with this low-level call since the adapter address is set by the admin and we've
// already checked that its not the zero address.
// solhint-disable-next-line avoid-low-level-calls
(bool success, ) = adapter.delegatecall(
abi.encodeWithSignature(
"relayMessage(address,bytes)",
spokePool, // target. This should be the spokePool on the L2.
abi.encodeWithSignature(
"relayRootBundle(bytes32,bytes32)",
rootBundleProposal.relayerRefundRoot,
rootBundleProposal.slowRelayRoot
) // message
)
);
require(success, "delegatecall failed");
}
// Transfer the bondAmount back to the proposer, if this the last executed leaf. Only sending this once all
// leaves have been executed acts to force the data worker to execute all bundles or they won't receive their bond.
if (rootBundleProposal.unclaimedPoolRebalanceLeafCount == 0)
bondToken.safeTransfer(rootBundleProposal.proposer, bondAmount);
emit RootBundleExecuted(
groupIndex,
leafId,
chainId,
l1Tokens,
bundleLpFees,
netSendAmounts,
runningBalances,
msg.sender
);
}
/**
* @notice Caller stakes a bond to dispute the current root bundle proposal assuming it has not passed liveness
* yet. The proposal is deleted, allowing a follow-up proposal to be submitted, and the dispute is sent to the
* optimistic oracle to be adjudicated. Can only be called within the liveness period of the current proposal.
* @notice The caller of this function must approve this contract to spend bondAmount of l1Token.
*/
function disputeRootBundle() public nonReentrant zeroOptimisticOracleApproval {
uint32 currentTime = uint32(getCurrentTime());
require(currentTime <= rootBundleProposal.challengePeriodEndTimestamp, "Request passed liveness");
// Request price from OO and dispute it.
uint256 finalFee = _getBondTokenFinalFee();
// This method will request a price from the OO and dispute it. Note that we set the ancillary data to
// the empty string (""). The root bundle that is being disputed was the most recently proposed one with a
// block number less than or equal to the dispute block time. All of this root bundle data can be found in
// the ProposeRootBundle event params. Moreover, the optimistic oracle will stamp the requester's address
// (i.e. this contract address) meaning that ancillary data for a dispute originating from another HubPool
// will always be distinct from a dispute originating from this HubPool. Moreover, since
// bundleEvaluationNumbers for a root bundle proposal are not stored in this contract, DVM voters will always
// have to look up the ProposeRootBundle event to evaluate a dispute, therefore there is no point emitting extra
// data in this ancillary data that is already included in the ProposeRootBundle event.
// If the finalFee is larger than the bond amount, the bond amount needs to be reset before a request can go
// through. Cancel to avoid a revert. Similarly, if the final fee == bond amount, then the proposer bond
// set in the optimistic oracle would be 0. The optimistic oracle would then default the bond to be equal
// to the final fee, which would mean that the allowance set to the bondAmount would be insufficient and the
// requestAndProposePriceFor() call would revert. Source: https://github.com/UMAprotocol/protocol/blob/5b37ea818a28479c01e458389a83c3e736306b17/packages/core/contracts/oracle/implementation/SkinnyOptimisticOracle.sol#L321
if (finalFee >= bondAmount) {
_cancelBundle();
return;
}
SkinnyOptimisticOracleInterface optimisticOracle = _getOptimisticOracle();
// Only approve exact tokens to avoid more tokens than expected being pulled into the OptimisticOracle.
bondToken.safeIncreaseAllowance(address(optimisticOracle), bondAmount);
try
optimisticOracle.requestAndProposePriceFor(
identifier,
currentTime,
"",
bondToken,
// Set reward to 0, since we'll settle proposer reward payouts directly from this contract after a root
// proposal has passed the challenge period.
0,
// Set the Optimistic oracle proposer bond for the request. We can assume that bondAmount > finalFee.
bondAmount - finalFee,
// Set the Optimistic oracle liveness for the price request.
liveness,
rootBundleProposal.proposer,
// Canonical value representing "True"; i.e. the proposed relay is valid.
int256(1e18)
)
returns (uint256) {
// Ensure that approval == 0 after the call so the increaseAllowance call below doesn't allow more tokens
// to transfer than intended.
bondToken.safeApprove(address(optimisticOracle), 0);
} catch {
// Cancel the bundle since the proposal failed.
_cancelBundle();
return;
}
// Dispute the request that we just sent.
SkinnyOptimisticOracleInterface.Request memory ooPriceRequest = SkinnyOptimisticOracleInterface.Request({
proposer: rootBundleProposal.proposer,
disputer: address(0),
currency: bondToken,
settled: false,
proposedPrice: int256(1e18),
resolvedPrice: 0,
expirationTime: currentTime + liveness,
reward: 0,
finalFee: finalFee,
bond: bondAmount - finalFee,
customLiveness: liveness
});
// Finally, delete the state pertaining to the active proposal so that another proposer can submit a new bundle.
delete rootBundleProposal;
bondToken.safeTransferFrom(msg.sender, address(this), bondAmount);
bondToken.safeIncreaseAllowance(address(optimisticOracle), bondAmount);
optimisticOracle.disputePriceFor(identifier, currentTime, "", ooPriceRequest, msg.sender, address(this));
emit RootBundleDisputed(msg.sender, currentTime);
}
/**
* @notice Send unclaimed accumulated protocol fees to fee capture address.
* @param l1Token Token whose protocol fees the caller wants to disburse.
*/
function claimProtocolFeesCaptured(address l1Token) public override nonReentrant {
uint256 _unclaimedAccumulatedProtocolFees = unclaimedAccumulatedProtocolFees[l1Token];
unclaimedAccumulatedProtocolFees[l1Token] = 0;
IERC20(l1Token).safeTransfer(protocolFeeCaptureAddress, _unclaimedAccumulatedProtocolFees);
emit ProtocolFeesCapturedClaimed(l1Token, _unclaimedAccumulatedProtocolFees);
}
/**
* @notice Conveniently queries which destination token is mapped to the hash of an l1 token + destination chain ID.
* @dev Admin must be considerate to the compatibility of originToken and destinationToken within the protocol. Some
* token implementations will not function correctly within the Across v2 system. For example ERC20s that charge
* fees will break internal accounting, ERC777 can cause some functions to revert and upgradable tokens can pose
* risks if the implementation is shifted between whitelisting and usage.
* @dev If the pool rebalance route is not whitelisted then this will return address(0).
* @param destinationChainId Where destination token is deployed.
* @param l1Token Ethereum version token.
* @return destinationToken address The destination token that is sent to spoke pools after this contract bridges
* the l1Token to the destination chain.
*/
function poolRebalanceRoute(uint256 destinationChainId, address l1Token)
external
view
override
returns (address destinationToken)
{
return poolRebalanceRoutes[_poolRebalanceRouteKey(l1Token, destinationChainId)];
}
/**
* @notice This function allows a caller to load the contract with raw ETH to perform L2 calls. This is needed for
* Arbitrum calls, but may also be needed for others.
* @dev This function cannot be included in a multicall transaction call because it is payable. A realistic
* situation where this might be an issue is if the caller is executing a PoolRebalanceLeaf that needs to relay
* messages to Arbitrum. Relaying messages to Arbitrum requires that this contract has an ETH balance, so in this
* case the caller would need to pre-load this contract with ETH before multicall-executing the leaf.
*/
function loadEthForL2Calls() public payable override {
/* solhint-disable-line no-empty-blocks */
}
/*************************************************
* INTERNAL FUNCTIONS *
*************************************************/
// Called when a dispute fails due to parameter changes. This effectively resets the state and cancels the request
// with no loss of funds, thereby enabling a new bundle to be added.
function _cancelBundle() internal {
bondToken.transfer(rootBundleProposal.proposer, bondAmount);
delete rootBundleProposal;
emit RootBundleCanceled(msg.sender, getCurrentTime());
}
function _getOptimisticOracle() internal view returns (SkinnyOptimisticOracleInterface) {
return
SkinnyOptimisticOracleInterface(finder.getImplementationAddress(OracleInterfaces.SkinnyOptimisticOracle));
}
function _getBondTokenFinalFee() internal view returns (uint256) {
return
StoreInterface(finder.getImplementationAddress(OracleInterfaces.Store))
.computeFinalFee(address(bondToken))
.rawValue;
}
// Note this method does a lot and wraps together the sending of tokens and updating the pooled token trackers. This
// is done as a gas saving so we don't need to iterate over the l1Tokens multiple times.
function _sendTokensToChainAndUpdatePooledTokenTrackers(
address adapter,
address spokePool,
uint256 chainId,
address[] memory l1Tokens,
int256[] memory netSendAmounts,
uint256[] memory bundleLpFees
) internal {
uint256 length = l1Tokens.length;
for (uint256 i = 0; i < length; ) {
address l1Token = l1Tokens[i];
// Validate the L1 -> L2 token route is stored. If it is not then the output of the bridging action
// could send tokens to the 0x0 address on the L2.
address l2Token = poolRebalanceRoutes[_poolRebalanceRouteKey(l1Token, chainId)];
require(l2Token != address(0), "Route not whitelisted");
// If the net send amount for this token is positive then: 1) send tokens from L1->L2 to facilitate the L2
// relayer refund, 2) Update the liquidity trackers for the associated pooled tokens.
if (netSendAmounts[i] > 0) {
// Perform delegatecall to use the adapter's code with this contract's context. Opt for delegatecall's
// complexity in exchange for lower gas costs.
// We are ok with this low-level call since the adapter address is set by the admin and we've
// already checked that its not the zero address.
// solhint-disable-next-line avoid-low-level-calls
(bool success, ) = adapter.delegatecall(
abi.encodeWithSignature(
"relayTokens(address,address,uint256,address)",
l1Token, // l1Token.
l2Token, // l2Token.
uint256(netSendAmounts[i]), // amount.
spokePool // to. This should be the spokePool.
)
);
require(success, "delegatecall failed");
// Liquid reserves is decreased by the amount sent. utilizedReserves is increased by the amount sent.
pooledTokens[l1Token].utilizedReserves += netSendAmounts[i];
pooledTokens[l1Token].liquidReserves -= uint256(netSendAmounts[i]);
}
// Allocate LP fees and protocol fees from the bundle to the associated pooled token trackers.
_allocateLpAndProtocolFees(l1Token, bundleLpFees[i]);
// L1 tokens length won't be > types(uint256).length, so use unchecked block to save gas. Based on the
// stress test results in /test/gas-analytics/HubPool.RootExecution.ts, the UMIP should limit the L1 token
// count in valid proposals to be ~100 so any PoolRebalanceLeaves with > 100 l1Tokens should not make it
// to this stage.
unchecked {
++i;
}
}
}
function _exchangeRateCurrent(address l1Token) internal returns (uint256) {
PooledToken storage pooledToken = pooledTokens[l1Token]; // Note this is storage so the state can be modified.
uint256 lpTokenTotalSupply = IERC20(pooledToken.lpToken).totalSupply();
if (lpTokenTotalSupply == 0) return 1e18; // initial rate is 1:1 between LP tokens and collateral.
// First, update fee counters and local accounting of finalized transfers from L2 -> L1.
_updateAccumulatedLpFees(pooledToken); // Accumulate all allocated fees from the last time this method was called.
_sync(l1Token); // Fetch any balance changes due to token bridging finalization and factor them in.
// ExchangeRate := (liquidReserves + utilizedReserves - undistributedLpFees) / lpTokenSupply
// Both utilizedReserves and undistributedLpFees contain assigned LP fees. UndistributedLpFees is gradually
// decreased over the smear duration using _updateAccumulatedLpFees. This means that the exchange rate will
// gradually increase over time as undistributedLpFees goes to zero.
// utilizedReserves can be negative. If this is the case, then liquidReserves is offset by an equal
// and opposite size. LiquidReserves + utilizedReserves will always be larger than undistributedLpFees so this
// int will always be positive so there is no risk in underflow in type casting in the return line.
int256 numerator = int256(pooledToken.liquidReserves) +
pooledToken.utilizedReserves -
int256(pooledToken.undistributedLpFees);
return (uint256(numerator) * 1e18) / lpTokenTotalSupply;
}
// Update internal fee counters by adding in any accumulated fees from the last time this logic was called.
function _updateAccumulatedLpFees(PooledToken storage pooledToken) internal {
uint256 accumulatedFees = _getAccumulatedFees(pooledToken.undistributedLpFees, pooledToken.lastLpFeeUpdate);
pooledToken.undistributedLpFees -= accumulatedFees;
pooledToken.lastLpFeeUpdate = uint32(getCurrentTime());
}
// Calculate the unallocated accumulatedFees from the last time the contract was called.
function _getAccumulatedFees(uint256 undistributedLpFees, uint256 lastLpFeeUpdate) internal view returns (uint256) {
// accumulatedFees := min(undistributedLpFees * lpFeeRatePerSecond * timeFromLastInteraction, undistributedLpFees)
// The min acts to pay out all fees in the case the equation returns more than the remaining fees.
uint256 timeFromLastInteraction = getCurrentTime() - lastLpFeeUpdate;
uint256 maxUndistributedLpFees = (undistributedLpFees * lpFeeRatePerSecond * timeFromLastInteraction) / (1e18);
return maxUndistributedLpFees < undistributedLpFees ? maxUndistributedLpFees : undistributedLpFees;
}
function _sync(address l1Token) internal {
// Check if the l1Token balance of the contract is greater than the liquidReserves. If it is then the bridging
// action from L2 -> L1 has concluded and the local accounting can be updated.
// Note: this calculation must take into account the bond when it's acting on the bond token and there's an
// active request.
uint256 balance = IERC20(l1Token).balanceOf(address(this));
uint256 balanceSansBond = l1Token == address(bondToken) && _activeRequest() ? balance - bondAmount : balance;
if (balanceSansBond > pooledTokens[l1Token].liquidReserves) {
// Note the numerical operation below can send utilizedReserves to negative. This can occur when tokens are
// dropped onto the contract, exceeding the liquidReserves.
pooledTokens[l1Token].utilizedReserves -= int256(balanceSansBond - pooledTokens[l1Token].liquidReserves);
pooledTokens[l1Token].liquidReserves = balanceSansBond;
}
}
function _liquidityUtilizationPostRelay(address l1Token, uint256 relayedAmount) internal returns (uint256) {
_sync(l1Token); // Fetch any balance changes due to token bridging finalization and factor them in.
// liquidityUtilizationRatio := (relayedAmount + max(utilizedReserves,0)) / (liquidReserves + max(utilizedReserves,0))
// UtilizedReserves has a dual meaning: if it's greater than zero then it represents funds pending in the bridge
// that will flow from L2 to L1. In this case, we can use it normally in the equation. However, if it is
// negative, then it is already counted in liquidReserves. This occurs if tokens are transferred directly to the
// contract. In this case, ignore it as it is captured in liquid reserves and has no meaning in the numerator.
PooledToken memory pooledL1Token = pooledTokens[l1Token];
uint256 flooredUtilizedReserves = pooledL1Token.utilizedReserves > 0
? uint256(pooledL1Token.utilizedReserves) // If positive: take the uint256 cast utilizedReserves.
: 0; // Else, if negative, then the is already captured in liquidReserves and should be ignored.
uint256 numerator = relayedAmount + flooredUtilizedReserves;
uint256 denominator = pooledL1Token.liquidReserves + flooredUtilizedReserves;
// If the denominator equals zero, return 1e18 (max utilization).
if (denominator == 0) return 1e18;
// In all other cases, return the utilization ratio.
return (numerator * 1e18) / denominator;
}
function _allocateLpAndProtocolFees(address l1Token, uint256 bundleLpFees) internal {
// Calculate the fraction of bundledLpFees that are allocated to the protocol and to the LPs.
uint256 protocolFeesCaptured = (bundleLpFees * protocolFeeCapturePct) / 1e18;
uint256 lpFeesCaptured = bundleLpFees - protocolFeesCaptured;
// Assign any LP fees included into the bundle to the pooled token. These LP fees are tracked in the
// undistributedLpFees and within the utilizedReserves. undistributedLpFees is gradually decreased
// over the smear duration to give the LPs their rewards over a period of time. Adding to utilizedReserves
// acts to track these rewards after the smear duration. See _exchangeRateCurrent for more details.
if (lpFeesCaptured > 0) {
pooledTokens[l1Token].undistributedLpFees += lpFeesCaptured;
pooledTokens[l1Token].utilizedReserves += int256(lpFeesCaptured);
}
// If there are any protocol fees, allocate them to the unclaimed protocol tracker amount.
if (protocolFeesCaptured > 0) unclaimedAccumulatedProtocolFees[l1Token] += protocolFeesCaptured;
}
function _relaySpokePoolAdminFunction(uint256 chainId, bytes memory functionData) internal {
(address adapter, address spokePool) = _getInitializedCrossChainContracts(chainId);
// Perform delegatecall to use the adapter's code with this contract's context.
// We are ok with this low-level call since the adapter address is set by the admin and we've
// already checked that its not the zero address.
// solhint-disable-next-line avoid-low-level-calls
(bool success, ) = adapter.delegatecall(
abi.encodeWithSignature(
"relayMessage(address,bytes)",
spokePool, // target. This should be the spokePool on the L2.
functionData
)
);
require(success, "delegatecall failed");
emit SpokePoolAdminFunctionTriggered(chainId, functionData);
}
function _poolRebalanceRouteKey(address l1Token, uint256 destinationChainId) internal pure returns (bytes32) {
return keccak256(abi.encode(l1Token, destinationChainId));
}
function _getInitializedCrossChainContracts(uint256 chainId)
internal
view
returns (address adapter, address spokePool)
{
adapter = crossChainContracts[chainId].adapter;
spokePool = crossChainContracts[chainId].spokePool;
require(spokePool != address(0), "SpokePool not initialized");
require(adapter.isContract(), "Adapter not initialized");
}
function _activeRequest() internal view returns (bool) {
return rootBundleProposal.unclaimedPoolRebalanceLeafCount != 0;
}
// If functionCallStackOriginatesFromOutsideThisContract is true then this was called by the callback function
// by dropping ETH onto the contract. In this case, deposit the ETH into WETH. This would happen if ETH was sent
// over the optimism bridge, for example. If false then this was set as a result of unwinding LP tokens, with the
// intention of sending ETH to the LP. In this case, do nothing as we intend on sending the ETH to the LP.
function _depositEthToWeth() internal {
if (functionCallStackOriginatesFromOutsideThisContract()) weth.deposit{ value: msg.value }();
}
// Added to enable the HubPool to receive ETH. This will occur both when the HubPool unwraps WETH to send to LPs and
// when ETH is sent over the canonical Optimism bridge, which sends ETH.
fallback() external payable {
_depositEthToWeth();
}
receive() external payable {
_depositEthToWeth();
}
}// SPDX-License-Identifier: GPL-3.0-only
pragma solidity ^0.8.0;
import "./SpokePoolInterface.sol";
import "./HubPoolInterface.sol";
import "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";
/**
* @notice Library to help with merkle roots, proofs, and claims.
*/
library MerkleLib {
/**
* @notice Verifies that a repayment is contained within a merkle root.
* @param root the merkle root.
* @param rebalance the rebalance struct.
* @param proof the merkle proof.
* @return bool to signal if the pool rebalance proof correctly shows inclusion of the rebalance within the tree.
*/
function verifyPoolRebalance(
bytes32 root,
HubPoolInterface.PoolRebalanceLeaf memory rebalance,
bytes32[] memory proof
) internal pure returns (bool) {
return MerkleProof.verify(proof, root, keccak256(abi.encode(rebalance)));
}
/**
* @notice Verifies that a relayer refund is contained within a merkle root.
* @param root the merkle root.
* @param refund the refund struct.
* @param proof the merkle proof.
* @return bool to signal if the relayer refund proof correctly shows inclusion of the refund within the tree.
*/
function verifyRelayerRefund(
bytes32 root,
SpokePoolInterface.RelayerRefundLeaf memory refund,
bytes32[] memory proof
) internal pure returns (bool) {
return MerkleProof.verify(proof, root, keccak256(abi.encode(refund)));
}
/**
* @notice Verifies that a distribution is contained within a merkle root.
* @param root the merkle root.
* @param slowRelayFulfillment the relayData fulfillment struct.
* @param proof the merkle proof.
* @return bool to signal if the slow relay's proof correctly shows inclusion of the slow relay within the tree.
*/
function verifySlowRelayFulfillment(
bytes32 root,
SpokePoolInterface.RelayData memory slowRelayFulfillment,
bytes32[] memory proof
) internal pure returns (bool) {
return MerkleProof.verify(proof, root, keccak256(abi.encode(slowRelayFulfillment)));
}
// The following functions are primarily copied from
// https://github.com/Uniswap/merkle-distributor/blob/master/contracts/MerkleDistributor.sol with minor changes.
/**
* @notice Tests whether a claim is contained within a claimedBitMap mapping.
* @param claimedBitMap a simple uint256 mapping in storage used as a bitmap.
* @param index the index to check in the bitmap.
* @return bool indicating if the index within the claimedBitMap has been marked as claimed.
*/
function isClaimed(mapping(uint256 => uint256) storage claimedBitMap, uint256 index) internal view returns (bool) {
uint256 claimedWordIndex = index / 256;
uint256 claimedBitIndex = index % 256;
uint256 claimedWord = claimedBitMap[claimedWordIndex];
uint256 mask = (1 << claimedBitIndex);
return claimedWord & mask == mask;
}
/**
* @notice Marks an index in a claimedBitMap as claimed.
* @param claimedBitMap a simple uint256 mapping in storage used as a bitmap.
* @param index the index to mark in the bitmap.
*/
function setClaimed(mapping(uint256 => uint256) storage claimedBitMap, uint256 index) internal {
uint256 claimedWordIndex = index / 256;
uint256 claimedBitIndex = index % 256;
claimedBitMap[claimedWordIndex] = claimedBitMap[claimedWordIndex] | (1 << claimedBitIndex);
}
/**
* @notice Tests whether a claim is contained within a 1D claimedBitMap mapping.
* @param claimedBitMap a simple uint256 value, encoding a 1D bitmap.
* @param index the index to check in the bitmap. Uint8 type enforces that index can't be > 255.
* @return bool indicating if the index within the claimedBitMap has been marked as claimed.
*/
function isClaimed1D(uint256 claimedBitMap, uint8 index) internal pure returns (bool) {
uint256 mask = (1 << index);
return claimedBitMap & mask == mask;
}
/**
* @notice Marks an index in a claimedBitMap as claimed.
* @param claimedBitMap a simple uint256 mapping in storage used as a bitmap. Uint8 type enforces that index
* can't be > 255.
* @param index the index to mark in the bitmap.
* @return uint256 representing the modified input claimedBitMap with the index set to true.
*/
function setClaimed1D(uint256 claimedBitMap, uint8 index) internal pure returns (uint256) {
return claimedBitMap | (1 << index % 256);
}
}// SPDX-License-Identifier: GPL-3.0-only
pragma solidity ^0.8.0;
import "./interfaces/AdapterInterface.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
/**
* @notice Concise list of functions in HubPool implementation.
*/
interface HubPoolInterface {
// This leaf is meant to be decoded in the HubPool to rebalance tokens between HubPool and SpokePool.
struct PoolRebalanceLeaf {
// This is used to know which chain to send cross-chain transactions to (and which SpokePool to send to).
uint256 chainId;
// Total LP fee amount per token in this bundle, encompassing all associated bundled relays.
uint256[] bundleLpFees;
// Represents the amount to push to or pull from the SpokePool. If +, the pool pays the SpokePool. If negative
// the SpokePool pays the HubPool. There can be arbitrarily complex rebalancing rules defined offchain. This
// number is only nonzero when the rules indicate that a rebalancing action should occur. When a rebalance does
// occur, runningBalances must be set to zero for this token and netSendAmounts should be set to the previous
// runningBalances + relays - deposits in this bundle. If non-zero then it must be set on the SpokePool's
// RelayerRefundLeaf amountToReturn as -1 * this value to show if funds are being sent from or to the SpokePool.
int256[] netSendAmounts;
// This is only here to be emitted in an event to track a running unpaid balance between the L2 pool and the L1
// pool. A positive number indicates that the HubPool owes the SpokePool funds. A negative number indicates that
// the SpokePool owes the HubPool funds. See the comment above for the dynamics of this and netSendAmounts.
int256[] runningBalances;
// Used by data worker to mark which leaves should relay roots to SpokePools, and to otherwise organize leaves.
// For example, each leaf should contain all the rebalance information for a single chain, but in the case where
// the list of l1Tokens is very large such that they all can't fit into a single leaf that can be executed under
// the block gas limit, then the data worker can use this groupIndex to organize them. Any leaves with
// a groupIndex equal to 0 will relay roots to the SpokePool, so the data worker should ensure that only one
// leaf for a specific chainId should have a groupIndex equal to 0.
uint256 groupIndex;
// Used as the index in the bitmap to track whether this leaf has been executed or not.
uint8 leafId;
// The bundleLpFees, netSendAmounts, and runningBalances are required to be the same length. They are parallel
// arrays for the given chainId and should be ordered by the l1Tokens field. All whitelisted tokens with nonzero
// relays on this chain in this bundle in the order of whitelisting.
address[] l1Tokens;
}
// A data worker can optimistically store several merkle roots on this contract by staking a bond and calling
// proposeRootBundle. By staking a bond, the data worker is alleging that the merkle roots all contain valid leaves
// that can be executed later to:
// - Send funds from this contract to a SpokePool or vice versa
// - Send funds from a SpokePool to Relayer as a refund for a relayed deposit
// - Send funds from a SpokePool to a deposit recipient to fulfill a "slow" relay
// Anyone can dispute this struct if the merkle roots contain invalid leaves before the
// challengePeriodEndTimestamp. Once the expiration timestamp is passed, executeRootBundle to execute a leaf
// from the poolRebalanceRoot on this contract and it will simultaneously publish the relayerRefundRoot and
// slowRelayRoot to a SpokePool. The latter two roots, once published to the SpokePool, contain
// leaves that can be executed on the SpokePool to pay relayers or recipients.
struct RootBundle {
// Contains leaves instructing this contract to send funds to SpokePools.
bytes32 poolRebalanceRoot;
// Relayer refund merkle root to be published to a SpokePool.
bytes32 relayerRefundRoot;
// Slow relay merkle root to be published to a SpokePool.
bytes32 slowRelayRoot;
// This is a 1D bitmap, with max size of 256 elements, limiting us to 256 chainsIds.
uint256 claimedBitMap;
// Proposer of this root bundle.
address proposer;
// Number of pool rebalance leaves to execute in the poolRebalanceRoot. After this number
// of leaves are executed, a new root bundle can be proposed
uint8 unclaimedPoolRebalanceLeafCount;
// When root bundle challenge period passes and this root bundle becomes executable.
uint32 challengePeriodEndTimestamp;
}
// Each whitelisted L1 token has an associated pooledToken struct that contains all information used to track the
// cumulative LP positions and if this token is enabled for deposits.
struct PooledToken {
// LP token given to LPs of a specific L1 token.
address lpToken;
// True if accepting new LP's.
bool isEnabled;
// Timestamp of last LP fee update.
uint32 lastLpFeeUpdate;
// Number of LP funds sent via pool rebalances to SpokePools and are expected to be sent
// back later.
int256 utilizedReserves;
// Number of LP funds held in contract less utilized reserves.
uint256 liquidReserves;
// Number of LP funds reserved to pay out to LPs as fees.
uint256 undistributedLpFees;
}
// Helper contracts to facilitate cross chain actions between HubPool and SpokePool for a specific network.
struct CrossChainContract {
address adapter;
address spokePool;
}
function setPaused(bool pause) external;
function emergencyDeleteProposal() external;
function relaySpokePoolAdminFunction(uint256 chainId, bytes memory functionData) external;
function setProtocolFeeCapture(address newProtocolFeeCaptureAddress, uint256 newProtocolFeeCapturePct) external;
function setBond(IERC20 newBondToken, uint256 newBondAmount) external;
function setLiveness(uint32 newLiveness) external;
function setIdentifier(bytes32 newIdentifier) external;
function setCrossChainContracts(
uint256 l2ChainId,
address adapter,
address spokePool
) external;
function enableL1TokenForLiquidityProvision(address l1Token) external;
function disableL1TokenForLiquidityProvision(address l1Token) external;
function addLiquidity(address l1Token, uint256 l1TokenAmount) external payable;
function removeLiquidity(
address l1Token,
uint256 lpTokenAmount,
bool sendEth
) external;
function exchangeRateCurrent(address l1Token) external returns (uint256);
function liquidityUtilizationCurrent(address l1Token) external returns (uint256);
function liquidityUtilizationPostRelay(address l1Token, uint256 relayedAmount) external returns (uint256);
function sync(address l1Token) external;
function proposeRootBundle(
uint256[] memory bundleEvaluationBlockNumbers,
uint8 poolRebalanceLeafCount,
bytes32 poolRebalanceRoot,
bytes32 relayerRefundRoot,
bytes32 slowRelayRoot
) external;
function executeRootBundle(
uint256 chainId,
uint256 groupIndex,
uint256[] memory bundleLpFees,
int256[] memory netSendAmounts,
int256[] memory runningBalances,
uint8 leafId,
address[] memory l1Tokens,
bytes32[] memory proof
) external;
function disputeRootBundle() external;
function claimProtocolFeesCaptured(address l1Token) external;
function setPoolRebalanceRoute(
uint256 destinationChainId,
address l1Token,
address destinationToken
) external;
function setDepositRoute(
uint256 originChainId,
uint256 destinationChainId,
address originToken,
bool depositsEnabled
) external;
function poolRebalanceRoute(uint256 destinationChainId, address l1Token)
external
view
returns (address destinationToken);
function loadEthForL2Calls() external payable;
}// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.0;
/**
* @title A contract that provides modifiers to prevent reentrancy to state-changing and view-only methods. This contract
* is inspired by https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/ReentrancyGuard.sol
* and https://github.com/balancer-labs/balancer-core/blob/master/contracts/BPool.sol.
* @dev The reason why we use this local contract instead of importing from uma/contracts is because of the addition
* of the internal method `functionCallStackOriginatesFromOutsideThisContract` which doesn't exist in the one exported
* by uma/contracts.
*/
contract Lockable {
bool internal _notEntered;
constructor() {
// Storing an initial non-zero value makes deployment a bit more expensive, but in exchange the refund on every
// call to nonReentrant will be lower in amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to increase the likelihood of the full
// refund coming into effect.
_notEntered = true;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a nonReentrant function from another nonReentrant function is not supported. It is possible to
* prevent this from happening by making the nonReentrant function external, and making it call a private
* function that does the actual state modification.
*/
modifier nonReentrant() {
_preEntranceCheck();
_preEntranceSet();
_;
_postEntranceReset();
}
/**
* @dev Designed to prevent a view-only method from being re-entered during a call to a nonReentrant() state-changing method.
*/
modifier nonReentrantView() {
_preEntranceCheck();
_;
}
/**
* @dev Returns true if the contract is currently in a non-entered state, meaning that the origination of the call
* came from outside the contract. This is relevant with fallback/receive methods to see if the call came from ETH
* being dropped onto the contract externally or due to ETH dropped on the the contract from within a method in this
* contract, such as unwrapping WETH to ETH within the contract.
*/
function functionCallStackOriginatesFromOutsideThisContract() internal view returns (bool) {
return _notEntered;
}
// Internal methods are used to avoid copying the require statement's bytecode to every nonReentrant() method.
// On entry into a function, _preEntranceCheck() should always be called to check if the function is being
// re-entered. Then, if the function modifies state, it should call _postEntranceSet(), perform its logic, and
// then call _postEntranceReset().
// View-only methods can simply call _preEntranceCheck() to make sure that it is not being re-entered.
function _preEntranceCheck() internal view {
// On the first call to nonReentrant, _notEntered will be true
require(_notEntered, "ReentrancyGuard: reentrant call");
}
function _preEntranceSet() internal {
// Any calls to nonReentrant after this point will fail
_notEntered = false;
}
function _postEntranceReset() internal {
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_notEntered = true;
}
}// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.0;
interface LpTokenFactoryInterface {
function createLpToken(address l1Token) external returns (address);
}// SPDX-License-Identifier: GPL-3.0-only
pragma solidity ^0.8.0;
interface WETH9 {
function withdraw(uint256 wad) external;
function deposit() external payable;
function balanceOf(address guy) external view returns (uint256 wad);
function transfer(address guy, uint256 wad) external;
}// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.0;
import "./Timer.sol";
/**
* @title Base class that provides time overrides, but only if being run in test mode.
*/
abstract contract Testable {
// If the contract is being run in production, then `timerAddress` will be the 0x0 address.
// Note: this variable should be set on construction and never modified.
address public timerAddress;
/**
* @notice Constructs the Testable contract. Called by child contracts.
* @param _timerAddress Contract that stores the current time in a testing environment.
* Must be set to 0x0 for production environments that use live time.
*/
constructor(address _timerAddress) {
timerAddress = _timerAddress;
}
/**
* @notice Reverts if not running in test mode.
*/
modifier onlyIfTest {
require(timerAddress != address(0x0));
_;
}
/**
* @notice Sets the current time.
* @dev Will revert if not running in test mode.
* @param time timestamp to set current Testable time to.
*/
function setCurrentTime(uint256 time) external onlyIfTest {
Timer(timerAddress).setCurrentTime(time);
}
/**
* @notice Gets the current time. Will return the last time set in `setCurrentTime` if running in test mode.
* Otherwise, it will return the block timestamp.
* @return uint for the current Testable timestamp.
*/
function getCurrentTime() public view virtual returns (uint256) {
if (timerAddress != address(0x0)) {
return Timer(timerAddress).getCurrentTime();
} else {
return block.timestamp; // solhint-disable-line not-rely-on-time
}
}
}// This contract is taken from Uniswaps's multi call implementation (https://github.com/Uniswap/uniswap-v3-periphery/blob/main/contracts/base/Multicall.sol)
// and was modified to be solidity 0.8 compatible. Additionally, the method was restricted to only work with msg.value
// set to 0 to avoid any nasty attack vectors on function calls that use value sent with deposits.
pragma solidity ^0.8.0;
/// @title MultiCaller
/// @notice Enables calling multiple methods in a single call to the contract
contract MultiCaller {
function multicall(bytes[] calldata data) external payable returns (bytes[] memory results) {
require(msg.value == 0, "Only multicall with 0 value");
results = new bytes[](data.length);
for (uint256 i = 0; i < data.length; i++) {
(bool success, bytes memory result) = address(this).delegatecall(data[i]);
if (!success) {
// Next 5 lines from https://ethereum.stackexchange.com/a/83577
if (result.length < 68) revert();
assembly {
result := add(result, 0x04)
}
revert(abi.decode(result, (string)));
}
results[i] = result;
}
}
}// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.0;
/**
* @title Stores common interface names used throughout the DVM by registration in the Finder.
*/
library OracleInterfaces {
bytes32 public constant Oracle = "Oracle";
bytes32 public constant IdentifierWhitelist = "IdentifierWhitelist";
bytes32 public constant Store = "Store";
bytes32 public constant FinancialContractsAdmin = "FinancialContractsAdmin";
bytes32 public constant Registry = "Registry";
bytes32 public constant CollateralWhitelist = "CollateralWhitelist";
bytes32 public constant OptimisticOracle = "OptimisticOracle";
bytes32 public constant Bridge = "Bridge";
bytes32 public constant GenericHandler = "GenericHandler";
bytes32 public constant SkinnyOptimisticOracle = "SkinnyOptimisticOracle";
bytes32 public constant ChildMessenger = "ChildMessenger";
bytes32 public constant OracleHub = "OracleHub";
bytes32 public constant OracleSpoke = "OracleSpoke";
}
/**
* @title Commonly re-used values for contracts associated with the OptimisticOracle.
*/
library OptimisticOracleConstraints {
// Any price request submitted to the OptimisticOracle must contain ancillary data no larger than this value.
// This value must be <= the Voting contract's `ancillaryBytesLimit` constant value otherwise it is possible
// that a price can be requested to the OptimisticOracle successfully, but cannot be resolved by the DVM which
// refuses to accept a price request made with ancillary data length over a certain size.
uint256 public constant ancillaryBytesLimit = 8192;
}// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.0;
interface AddressWhitelistInterface {
function addToWhitelist(address newElement) external;
function removeFromWhitelist(address newElement) external;
function isOnWhitelist(address newElement) external view returns (bool);
function getWhitelist() external view returns (address[] memory);
}// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.0;
/**
* @title Interface for whitelists of supported identifiers that the oracle can provide prices for.
*/
interface IdentifierWhitelistInterface {
/**
* @notice Adds the provided identifier as a supported identifier.
* @dev Price requests using this identifier will succeed after this call.
* @param identifier bytes32 encoding of the string identifier. Eg: BTC/USD.
*/
function addSupportedIdentifier(bytes32 identifier) external;
/**
* @notice Removes the identifier from the whitelist.
* @dev Price requests using this identifier will no longer succeed after this call.
* @param identifier bytes32 encoding of the string identifier. Eg: BTC/USD.
*/
function removeSupportedIdentifier(bytes32 identifier) external;
/**
* @notice Checks whether an identifier is on the whitelist.
* @param identifier bytes32 encoding of the string identifier. Eg: BTC/USD.
* @return bool if the identifier is supported (or not).
*/
function isIdentifierSupported(bytes32 identifier) external view returns (bool);
}// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.0;
/**
* @title Provides addresses of the live contracts implementing certain interfaces.
* @dev Examples are the Oracle or Store interfaces.
*/
interface FinderInterface {
/**
* @notice Updates the address of the contract that implements `interfaceName`.
* @param interfaceName bytes32 encoding of the interface name that is either changed or registered.
* @param implementationAddress address of the deployed contract that implements the interface.
*/
function changeImplementationAddress(bytes32 interfaceName, address implementationAddress) external;
/**
* @notice Gets the address of the contract that implements the given `interfaceName`.
* @param interfaceName queried interface.
* @return implementationAddress address of the deployed contract that implements the interface.
*/
function getImplementationAddress(bytes32 interfaceName) external view returns (address);
}// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "../../common/implementation/FixedPoint.sol";
/**
* @title Interface that allows financial contracts to pay oracle fees for their use of the system.
*/
interface StoreInterface {
/**
* @notice Pays Oracle fees in ETH to the store.
* @dev To be used by contracts whose margin currency is ETH.
*/
function payOracleFees() external payable;
/**
* @notice Pays oracle fees in the margin currency, erc20Address, to the store.
* @dev To be used if the margin currency is an ERC20 token rather than ETH.
* @param erc20Address address of the ERC20 token used to pay the fee.
* @param amount number of tokens to transfer. An approval for at least this amount must exist.
*/
function payOracleFeesErc20(address erc20Address, FixedPoint.Unsigned calldata amount) external;
/**
* @notice Computes the regular oracle fees that a contract should pay for a period.
* @param startTime defines the beginning time from which the fee is paid.
* @param endTime end time until which the fee is paid.
* @param pfc "profit from corruption", or the maximum amount of margin currency that a
* token sponsor could extract from the contract through corrupting the price feed in their favor.
* @return regularFee amount owed for the duration from start to end time for the given pfc.
* @return latePenalty for paying the fee after the deadline.
*/
function computeRegularFee(
uint256 startTime,
uint256 endTime,
FixedPoint.Unsigned calldata pfc
) external view returns (FixedPoint.Unsigned memory regularFee, FixedPoint.Unsigned memory latePenalty);
/**
* @notice Computes the final oracle fees that a contract should pay at settlement.
* @param currency token used to pay the final fee.
* @return finalFee amount due.
*/
function computeFinalFee(address currency) external view returns (FixedPoint.Unsigned memory);
}// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "../interfaces/OptimisticOracleInterface.sol";
/**
* @title Interface for the gas-cost-reduced version of the OptimisticOracle.
* @notice Differences from normal OptimisticOracle:
* - refundOnDispute: flag is removed, by default there are no refunds on disputes.
* - customizing request parameters: In the OptimisticOracle, parameters like `bond` and `customLiveness` can be reset
* after a request is already made via `requestPrice`. In the SkinnyOptimisticOracle, these parameters can only be
* set in `requestPrice`, which has an expanded input set.
* - settleAndGetPrice: Replaced by `settle`, which can only be called once per settleable request. The resolved price
* can be fetched via the `Settle` event or the return value of `settle`.
* - general changes to interface: Functions that interact with existing requests all require the parameters of the
* request to modify to be passed as input. These parameters must match with the existing request parameters or the
* function will revert. This change reflects the internal refactor to store hashed request parameters instead of the
* full request struct.
* @dev Interface used by financial contracts to interact with the Oracle. Voters will use a different interface.
*/
abstract contract SkinnyOptimisticOracleInterface {
// Struct representing a price request. Note that this differs from the OptimisticOracleInterface's Request struct
// in that refundOnDispute is removed.
struct Request {
address proposer; // Address of the proposer.
address disputer; // Address of the disputer.
IERC20 currency; // ERC20 token used to pay rewards and fees.
bool settled; // True if the request is settled.
int256 proposedPrice; // Price that the proposer submitted.
int256 resolvedPrice; // Price resolved once the request is settled.
uint256 expirationTime; // Time at which the request auto-settles without a dispute.
uint256 reward; // Amount of the currency to pay to the proposer on settlement.
uint256 finalFee; // Final fee to pay to the Store upon request to the DVM.
uint256 bond; // Bond that the proposer and disputer must pay on top of the final fee.
uint256 customLiveness; // Custom liveness value set by the requester.
}
// This value must be <= the Voting contract's `ancillaryBytesLimit` value otherwise it is possible
// that a price can be requested to this contract successfully, but cannot be disputed because the DVM refuses
// to accept a price request made with ancillary data length over a certain size.
uint256 public constant ancillaryBytesLimit = 8192;
/**
* @notice Requests a new price.
* @param identifier price identifier being requested.
* @param timestamp timestamp of the price being requested.
* @param ancillaryData ancillary data representing additional args being passed with the price request.
* @param currency ERC20 token used for payment of rewards and fees. Must be approved for use with the DVM.
* @param reward reward offered to a successful proposer. Will be pulled from the caller. Note: this can be 0,
* which could make sense if the contract requests and proposes the value in the same call or
* provides its own reward system.
* @param bond custom proposal bond to set for request. If set to 0, defaults to the final fee.
* @param customLiveness custom proposal liveness to set for request.
* @return totalBond default bond + final fee that the proposer and disputer will be required to pay.
*/
function requestPrice(
bytes32 identifier,
uint32 timestamp,
bytes memory ancillaryData,
IERC20 currency,
uint256 reward,
uint256 bond,
uint256 customLiveness
) external virtual returns (uint256 totalBond);
/**
* @notice Proposes a price value on another address' behalf. Note: this address will receive any rewards that come
* from this proposal. However, any bonds are pulled from the caller.
* @param requester sender of the initial price request.
* @param identifier price identifier to identify the existing request.
* @param timestamp timestamp to identify the existing request.
* @param ancillaryData ancillary data of the price being requested.
* @param request price request parameters whose hash must match the request that the caller wants to
* propose a price for.
* @param proposer address to set as the proposer.
* @param proposedPrice price being proposed.
* @return totalBond the amount that's pulled from the caller's wallet as a bond. The bond will be returned to
* the proposer once settled if the proposal is correct.
*/
function proposePriceFor(
address requester,
bytes32 identifier,
uint32 timestamp,
bytes memory ancillaryData,
Request memory request,
address proposer,
int256 proposedPrice
) public virtual returns (uint256 totalBond);
/**
* @notice Proposes a price value where caller is the proposer.
* @param requester sender of the initial price request.
* @param identifier price identifier to identify the existing request.
* @param timestamp timestamp to identify the existing request.
* @param ancillaryData ancillary data of the price being requested.
* @param request price request parameters whose hash must match the request that the caller wants to
* propose a price for.
* @param proposedPrice price being proposed.
* @return totalBond the amount that's pulled from the caller's wallet as a bond. The bond will be returned to
* the proposer once settled if the proposal is correct.
*/
function proposePrice(
address requester,
bytes32 identifier,
uint32 timestamp,
bytes memory ancillaryData,
Request memory request,
int256 proposedPrice
) external virtual returns (uint256 totalBond);
/**
* @notice Combines logic of requestPrice and proposePrice while taking advantage of gas savings from not having to
* overwrite Request params that a normal requestPrice() => proposePrice() flow would entail. Note: The proposer
* will receive any rewards that come from this proposal. However, any bonds are pulled from the caller.
* @dev The caller is the requester, but the proposer can be customized.
* @param identifier price identifier to identify the existing request.
* @param timestamp timestamp to identify the existing request.
* @param ancillaryData ancillary data of the price being requested.
* @param currency ERC20 token used for payment of rewards and fees. Must be approved for use with the DVM.
* @param reward reward offered to a successful proposer. Will be pulled from the caller. Note: this can be 0,
* which could make sense if the contract requests and proposes the value in the same call or
* provides its own reward system.
* @param bond custom proposal bond to set for request. If set to 0, defaults to the final fee.
* @param customLiveness custom proposal liveness to set for request.
* @param proposer address to set as the proposer.
* @param proposedPrice price being proposed.
* @return totalBond the amount that's pulled from the caller's wallet as a bond. The bond will be returned to
* the proposer once settled if the proposal is correct.
*/
function requestAndProposePriceFor(
bytes32 identifier,
uint32 timestamp,
bytes memory ancillaryData,
IERC20 currency,
uint256 reward,
uint256 bond,
uint256 customLiveness,
address proposer,
int256 proposedPrice
) external virtual returns (uint256 totalBond);
/**
* @notice Disputes a price request with an active proposal on another address' behalf. Note: this address will
* receive any rewards that come from this dispute. However, any bonds are pulled from the caller.
* @param identifier price identifier to identify the existing request.
* @param timestamp timestamp to identify the existing request.
* @param ancillaryData ancillary data of the price being requested.
* @param request price request parameters whose hash must match the request that the caller wants to
* dispute.
* @param disputer address to set as the disputer.
* @param requester sender of the initial price request.
* @return totalBond the amount that's pulled from the caller's wallet as a bond. The bond will be returned to
* the disputer once settled if the dispute was valid (the proposal was incorrect).
*/
function disputePriceFor(
bytes32 identifier,
uint32 timestamp,
bytes memory ancillaryData,
Request memory request,
address disputer,
address requester
) public virtual returns (uint256 totalBond);
/**
* @notice Disputes a price request with an active proposal where caller is the disputer.
* @param requester sender of the initial price request.
* @param identifier price identifier to identify the existing request.
* @param timestamp timestamp to identify the existing request.
* @param ancillaryData ancillary data of the price being requested.
* @param request price request parameters whose hash must match the request that the caller wants to
* dispute.
* @return totalBond the amount that's pulled from the caller's wallet as a bond. The bond will be returned to
* the disputer once settled if the dispute was valid (the proposal was incorrect).
*/
function disputePrice(
address requester,
bytes32 identifier,
uint32 timestamp,
bytes memory ancillaryData,
Request memory request
) external virtual returns (uint256 totalBond);
/**
* @notice Attempts to settle an outstanding price request. Will revert if it isn't settleable.
* @param requester sender of the initial price request.
* @param identifier price identifier to identify the existing request.
* @param timestamp timestamp to identify the existing request.
* @param ancillaryData ancillary data of the price being requested.
* @param request price request parameters whose hash must match the request that the caller wants to
* settle.
* @return payout the amount that the "winner" (proposer or disputer) receives on settlement. This amount includes
* the returned bonds as well as additional rewards.
* @return resolvedPrice the price that the request settled to.
*/
function settle(
address requester,
bytes32 identifier,
uint32 timestamp,
bytes memory ancillaryData,
Request memory request
) external virtual returns (uint256 payout, int256 resolvedPrice);
/**
* @notice Computes the current state of a price request. See the State enum for more details.
* @param requester sender of the initial price request.
* @param identifier price identifier to identify the existing request.
* @param timestamp timestamp to identify the existing request.
* @param ancillaryData ancillary data of the price being requested.
* @param request price request parameters.
* @return the State.
*/
function getState(
address requester,
bytes32 identifier,
uint32 timestamp,
bytes memory ancillaryData,
Request memory request
) external virtual returns (OptimisticOracleInterface.State);
/**
* @notice Checks if a given request has resolved, expired or been settled (i.e the optimistic oracle has a price).
* @param requester sender of the initial price request.
* @param identifier price identifier to identify the existing request.
* @param timestamp timestamp to identify the existing request.
* @param ancillaryData ancillary data of the price being requested.
* @param request price request parameters. The hash of these parameters must match with the request hash that is
* associated with the price request unique ID {requester, identifier, timestamp, ancillaryData}, or this method
* will revert.
* @return boolean indicating true if price exists and false if not.
*/
function hasPrice(
address requester,
bytes32 identifier,
uint32 timestamp,
bytes memory ancillaryData,
Request memory request
) public virtual returns (bool);
/**
* @notice Generates stamped ancillary data in the format that it would be used in the case of a price dispute.
* @param ancillaryData ancillary data of the price being requested.
* @param requester sender of the initial price request.
* @return the stamped ancillary bytes.
*/
function stampAncillaryData(bytes memory ancillaryData, address requester)
public
pure
virtual
returns (bytes memory);
}// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
/**
* @title ERC20 interface that includes burn and mint methods.
*/
abstract contract ExpandedIERC20 is IERC20 {
/**
* @notice Burns a specific amount of the caller's tokens.
* @dev Only burns the caller's tokens, so it is safe to leave this method permissionless.
*/
function burn(uint256 value) external virtual;
/**
* @dev Burns `value` tokens owned by `recipient`.
* @param recipient address to burn tokens from.
* @param value amount of tokens to burn.
*/
function burnFrom(address recipient, uint256 value) external virtual returns (bool);
/**
* @notice Mints tokens and adds them to the balance of the `to` address.
* @dev This method should be permissioned to only allow designated parties to mint tokens.
*/
function mint(address to, uint256 value) external virtual returns (bool);
function addMinter(address account) external virtual;
function addBurner(address account) external virtual;
function resetOwner(address account) external virtual;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)
pragma solidity ^0.8.0;
import "../utils/Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
constructor() {
_transferOwnership(_msgSender());
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
_;
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions anymore. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby removing any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(
address from,
address to,
uint256 amount
) external returns (bool);
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.0;
import "../IERC20.sol";
import "../../../utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using Address for address;
function safeTransfer(
IERC20 token,
address to,
uint256 value
) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
function safeTransferFrom(
IERC20 token,
address from,
address to,
uint256 value
) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
/**
* @dev Deprecated. This function has issues similar to the ones found in
* {IERC20-approve}, and its usage is discouraged.
*
* Whenever possible, use {safeIncreaseAllowance} and
* {safeDecreaseAllowance} instead.
*/
function safeApprove(
IERC20 token,
address spender,
uint256 value
) internal {
// safeApprove should only be called when setting an initial allowance,
// or when resetting it to zero. To increase and decrease it, use
// 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
require(
(value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
function safeIncreaseAllowance(
IERC20 token,
address spender,
uint256 value
) internal {
uint256 newAllowance = token.allowance(address(this), spender) + value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
function safeDecreaseAllowance(
IERC20 token,
address spender,
uint256 value
) internal {
unchecked {
uint256 oldAllowance = token.allowance(address(this), spender);
require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
uint256 newAllowance = oldAllowance - value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
if (returndata.length > 0) {
// Return data is optional
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol)
pragma solidity ^0.8.1;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0
// for contracts in construction, since the code is only stored at the end
// of the constructor execution.
return account.code.length > 0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCall(target, data, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value
) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
require(isContract(target), "Address: call to non-contract");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
require(isContract(target), "Address: static call to non-contract");
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
require(isContract(target), "Address: delegate call to non-contract");
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason using the provided one.
*
* _Available since v4.3._
*/
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}// SPDX-License-Identifier: GPL-3.0-only
pragma solidity ^0.8.0;
/**
* @notice Contains common data structures and functions used by all SpokePool implementations.
*/
interface SpokePoolInterface {
// This leaf is meant to be decoded in the SpokePool to pay out successful relayers.
struct RelayerRefundLeaf {
// This is the amount to return to the HubPool. This occurs when there is a PoolRebalanceLeaf netSendAmount that
// is negative. This is just the negative of this value.
uint256 amountToReturn;
// Used to verify that this is being executed on the correct destination chainId.
uint256 chainId;
// This array designates how much each of those addresses should be refunded.
uint256[] refundAmounts;
// Used as the index in the bitmap to track whether this leaf has been executed or not.
uint32 leafId;
// The associated L2TokenAddress that these claims apply to.
address l2TokenAddress;
// Must be same length as refundAmounts and designates each address that must be refunded.
address[] refundAddresses;
}
// This struct represents the data to fully specify a relay. If any portion of this data differs, the relay is
// considered to be completely distinct. Only one relay for a particular depositId, chainId pair should be
// considered valid and repaid. This data is hashed and inserted into the slow relay merkle root so that an off
// chain validator can choose when to refund slow relayers.
struct RelayData {
// The address that made the deposit on the origin chain.
address depositor;
// The recipient address on the destination chain.
address recipient;
// The corresponding token address on the destination chain.
address destinationToken;
// The total relay amount before fees are taken out.
uint256 amount;
// Origin chain id.
uint256 originChainId;
// Destination chain id.
uint256 destinationChainId;
// The LP Fee percentage computed by the relayer based on the deposit's quote timestamp
// and the HubPool's utilization.
uint64 realizedLpFeePct;
// The relayer fee percentage specified in the deposit.
uint64 relayerFeePct;
// The id uniquely identifying this deposit on the origin chain.
uint32 depositId;
}
// Stores collection of merkle roots that can be published to this contract from the HubPool, which are referenced
// by "data workers" via inclusion proofs to execute leaves in the roots.
struct RootBundle {
// Merkle root of slow relays that were not fully filled and whose recipient is still owed funds from the LP pool.
bytes32 slowRelayRoot;
// Merkle root of relayer refunds for successful relays.
bytes32 relayerRefundRoot;
// This is a 2D bitmap tracking which leaves in the relayer refund root have been claimed, with max size of
// 256x(2^248) leaves per root.
mapping(uint256 => uint256) claimedBitmap;
}
function setCrossDomainAdmin(address newCrossDomainAdmin) external;
function setHubPool(address newHubPool) external;
function setEnableRoute(
address originToken,
uint256 destinationChainId,
bool enable
) external;
function setDepositQuoteTimeBuffer(uint32 buffer) external;
function relayRootBundle(bytes32 relayerRefundRoot, bytes32 slowRelayRoot) external;
function emergencyDeleteRootBundle(uint256 rootBundleId) external;
function deposit(
address recipient,
address originToken,
uint256 amount,
uint256 destinationChainId,
uint64 relayerFeePct,
uint32 quoteTimestamp
) external payable;
function speedUpDeposit(
address depositor,
uint64 newRelayerFeePct,
uint32 depositId,
bytes memory depositorSignature
) external;
function fillRelay(
address depositor,
address recipient,
address destinationToken,
uint256 amount,
uint256 maxTokensToSend,
uint256 repaymentChainId,
uint256 originChainId,
uint64 realizedLpFeePct,
uint64 relayerFeePct,
uint32 depositId
) external;
function fillRelayWithUpdatedFee(
address depositor,
address recipient,
address destinationToken,
uint256 amount,
uint256 maxTokensToSend,
uint256 repaymentChainId,
uint256 originChainId,
uint64 realizedLpFeePct,
uint64 relayerFeePct,
uint64 newRelayerFeePct,
uint32 depositId,
bytes memory depositorSignature
) external;
function executeSlowRelayLeaf(
address depositor,
address recipient,
address destinationToken,
uint256 amount,
uint256 originChainId,
uint64 realizedLpFeePct,
uint64 relayerFeePct,
uint32 depositId,
uint32 rootBundleId,
bytes32[] memory proof
) external;
function executeRelayerRefundLeaf(
uint32 rootBundleId,
SpokePoolInterface.RelayerRefundLeaf memory relayerRefundLeaf,
bytes32[] memory proof
) external;
function chainId() external view returns (uint256);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (utils/cryptography/MerkleProof.sol)
pragma solidity ^0.8.0;
/**
* @dev These functions deal with verification of Merkle Trees proofs.
*
* The proofs can be generated using the JavaScript library
* https://github.com/miguelmota/merkletreejs[merkletreejs].
* Note: the hashing algorithm should be keccak256 and pair sorting should be enabled.
*
* See `test/utils/cryptography/MerkleProof.test.js` for some examples.
*/
library MerkleProof {
/**
* @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree
* defined by `root`. For this, a `proof` must be provided, containing
* sibling hashes on the branch from the leaf to the root of the tree. Each
* pair of leaves and each pair of pre-images are assumed to be sorted.
*/
function verify(
bytes32[] memory proof,
bytes32 root,
bytes32 leaf
) internal pure returns (bool) {
return processProof(proof, leaf) == root;
}
/**
* @dev Returns the rebuilt hash obtained by traversing a Merklee tree up
* from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt
* hash matches the root of the tree. When processing the proof, the pairs
* of leafs & pre-images are assumed to be sorted.
*
* _Available since v4.4._
*/
function processProof(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32) {
bytes32 computedHash = leaf;
for (uint256 i = 0; i < proof.length; i++) {
bytes32 proofElement = proof[i];
if (computedHash <= proofElement) {
// Hash(current computed hash + current element of the proof)
computedHash = _efficientHash(computedHash, proofElement);
} else {
// Hash(current element of the proof + current computed hash)
computedHash = _efficientHash(proofElement, computedHash);
}
}
return computedHash;
}
function _efficientHash(bytes32 a, bytes32 b) private pure returns (bytes32 value) {
assembly {
mstore(0x00, a)
mstore(0x20, b)
value := keccak256(0x00, 0x40)
}
}
}// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.0;
/**
* @notice Sends cross chain messages and tokens to contracts on a specific L2 network.
*/
interface AdapterInterface {
event MessageRelayed(address target, bytes message);
event TokensRelayed(address l1Token, address l2Token, uint256 amount, address to);
function relayMessage(address target, bytes calldata message) external payable;
function relayTokens(
address l1Token,
address l2Token,
uint256 amount,
address to
) external payable;
}// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.0;
/**
* @title Universal store of current contract time for testing environments.
*/
contract Timer {
uint256 private currentTime;
constructor() {
currentTime = block.timestamp; // solhint-disable-line not-rely-on-time
}
/**
* @notice Sets the current time.
* @dev Will revert if not running in test mode.
* @param time timestamp to set `currentTime` to.
*/
function setCurrentTime(uint256 time) external {
currentTime = time;
}
/**
* @notice Gets the currentTime variable set in the Timer.
* @return uint256 for the current Testable timestamp.
*/
function getCurrentTime() public view returns (uint256) {
return currentTime;
}
}// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/utils/math/SafeMath.sol";
import "@openzeppelin/contracts/utils/math/SignedSafeMath.sol";
/**
* @title Library for fixed point arithmetic on uints
*/
library FixedPoint {
using SafeMath for uint256;
using SignedSafeMath for int256;
// Supports 18 decimals. E.g., 1e18 represents "1", 5e17 represents "0.5".
// For unsigned values:
// This can represent a value up to (2^256 - 1)/10^18 = ~10^59. 10^59 will be stored internally as uint256 10^77.
uint256 private constant FP_SCALING_FACTOR = 10**18;
// --------------------------------------- UNSIGNED -----------------------------------------------------------------------------
struct Unsigned {
uint256 rawValue;
}
/**
* @notice Constructs an `Unsigned` from an unscaled uint, e.g., `b=5` gets stored internally as `5*(10**18)`.
* @param a uint to convert into a FixedPoint.
* @return the converted FixedPoint.
*/
function fromUnscaledUint(uint256 a) internal pure returns (Unsigned memory) {
return Unsigned(a.mul(FP_SCALING_FACTOR));
}
/**
* @notice Whether `a` is equal to `b`.
* @param a a FixedPoint.
* @param b a uint256.
* @return True if equal, or False.
*/
function isEqual(Unsigned memory a, uint256 b) internal pure returns (bool) {
return a.rawValue == fromUnscaledUint(b).rawValue;
}
/**
* @notice Whether `a` is equal to `b`.
* @param a a FixedPoint.
* @param b a FixedPoint.
* @return True if equal, or False.
*/
function isEqual(Unsigned memory a, Unsigned memory b) internal pure returns (bool) {
return a.rawValue == b.rawValue;
}
/**
* @notice Whether `a` is greater than `b`.
* @param a a FixedPoint.
* @param b a FixedPoint.
* @return True if `a > b`, or False.
*/
function isGreaterThan(Unsigned memory a, Unsigned memory b) internal pure returns (bool) {
return a.rawValue > b.rawValue;
}
/**
* @notice Whether `a` is greater than `b`.
* @param a a FixedPoint.
* @param b a uint256.
* @return True if `a > b`, or False.
*/
function isGreaterThan(Unsigned memory a, uint256 b) internal pure returns (bool) {
return a.rawValue > fromUnscaledUint(b).rawValue;
}
/**
* @notice Whether `a` is greater than `b`.
* @param a a uint256.
* @param b a FixedPoint.
* @return True if `a > b`, or False.
*/
function isGreaterThan(uint256 a, Unsigned memory b) internal pure returns (bool) {
return fromUnscaledUint(a).rawValue > b.rawValue;
}
/**
* @notice Whether `a` is greater than or equal to `b`.
* @param a a FixedPoint.
* @param b a FixedPoint.
* @return True if `a >= b`, or False.
*/
function isGreaterThanOrEqual(Unsigned memory a, Unsigned memory b) internal pure returns (bool) {
return a.rawValue >= b.rawValue;
}
/**
* @notice Whether `a` is greater than or equal to `b`.
* @param a a FixedPoint.
* @param b a uint256.
* @return True if `a >= b`, or False.
*/
function isGreaterThanOrEqual(Unsigned memory a, uint256 b) internal pure returns (bool) {
return a.rawValue >= fromUnscaledUint(b).rawValue;
}
/**
* @notice Whether `a` is greater than or equal to `b`.
* @param a a uint256.
* @param b a FixedPoint.
* @return True if `a >= b`, or False.
*/
function isGreaterThanOrEqual(uint256 a, Unsigned memory b) internal pure returns (bool) {
return fromUnscaledUint(a).rawValue >= b.rawValue;
}
/**
* @notice Whether `a` is less than `b`.
* @param a a FixedPoint.
* @param b a FixedPoint.
* @return True if `a < b`, or False.
*/
function isLessThan(Unsigned memory a, Unsigned memory b) internal pure returns (bool) {
return a.rawValue < b.rawValue;
}
/**
* @notice Whether `a` is less than `b`.
* @param a a FixedPoint.
* @param b a uint256.
* @return True if `a < b`, or False.
*/
function isLessThan(Unsigned memory a, uint256 b) internal pure returns (bool) {
return a.rawValue < fromUnscaledUint(b).rawValue;
}
/**
* @notice Whether `a` is less than `b`.
* @param a a uint256.
* @param b a FixedPoint.
* @return True if `a < b`, or False.
*/
function isLessThan(uint256 a, Unsigned memory b) internal pure returns (bool) {
return fromUnscaledUint(a).rawValue < b.rawValue;
}
/**
* @notice Whether `a` is less than or equal to `b`.
* @param a a FixedPoint.
* @param b a FixedPoint.
* @return True if `a <= b`, or False.
*/
function isLessThanOrEqual(Unsigned memory a, Unsigned memory b) internal pure returns (bool) {
return a.rawValue <= b.rawValue;
}
/**
* @notice Whether `a` is less than or equal to `b`.
* @param a a FixedPoint.
* @param b a uint256.
* @return True if `a <= b`, or False.
*/
function isLessThanOrEqual(Unsigned memory a, uint256 b) internal pure returns (bool) {
return a.rawValue <= fromUnscaledUint(b).rawValue;
}
/**
* @notice Whether `a` is less than or equal to `b`.
* @param a a uint256.
* @param b a FixedPoint.
* @return True if `a <= b`, or False.
*/
function isLessThanOrEqual(uint256 a, Unsigned memory b) internal pure returns (bool) {
return fromUnscaledUint(a).rawValue <= b.rawValue;
}
/**
* @notice The minimum of `a` and `b`.
* @param a a FixedPoint.
* @param b a FixedPoint.
* @return the minimum of `a` and `b`.
*/
function min(Unsigned memory a, Unsigned memory b) internal pure returns (Unsigned memory) {
return a.rawValue < b.rawValue ? a : b;
}
/**
* @notice The maximum of `a` and `b`.
* @param a a FixedPoint.
* @param b a FixedPoint.
* @return the maximum of `a` and `b`.
*/
function max(Unsigned memory a, Unsigned memory b) internal pure returns (Unsigned memory) {
return a.rawValue > b.rawValue ? a : b;
}
/**
* @notice Adds two `Unsigned`s, reverting on overflow.
* @param a a FixedPoint.
* @param b a FixedPoint.
* @return the sum of `a` and `b`.
*/
function add(Unsigned memory a, Unsigned memory b) internal pure returns (Unsigned memory) {
return Unsigned(a.rawValue.add(b.rawValue));
}
/**
* @notice Adds an `Unsigned` to an unscaled uint, reverting on overflow.
* @param a a FixedPoint.
* @param b a uint256.
* @return the sum of `a` and `b`.
*/
function add(Unsigned memory a, uint256 b) internal pure returns (Unsigned memory) {
return add(a, fromUnscaledUint(b));
}
/**
* @notice Subtracts two `Unsigned`s, reverting on overflow.
* @param a a FixedPoint.
* @param b a FixedPoint.
* @return the difference of `a` and `b`.
*/
function sub(Unsigned memory a, Unsigned memory b) internal pure returns (Unsigned memory) {
return Unsigned(a.rawValue.sub(b.rawValue));
}
/**
* @notice Subtracts an unscaled uint256 from an `Unsigned`, reverting on overflow.
* @param a a FixedPoint.
* @param b a uint256.
* @return the difference of `a` and `b`.
*/
function sub(Unsigned memory a, uint256 b) internal pure returns (Unsigned memory) {
return sub(a, fromUnscaledUint(b));
}
/**
* @notice Subtracts an `Unsigned` from an unscaled uint256, reverting on overflow.
* @param a a uint256.
* @param b a FixedPoint.
* @return the difference of `a` and `b`.
*/
function sub(uint256 a, Unsigned memory b) internal pure returns (Unsigned memory) {
return sub(fromUnscaledUint(a), b);
}
/**
* @notice Multiplies two `Unsigned`s, reverting on overflow.
* @dev This will "floor" the product.
* @param a a FixedPoint.
* @param b a FixedPoint.
* @return the product of `a` and `b`.
*/
function mul(Unsigned memory a, Unsigned memory b) internal pure returns (Unsigned memory) {
// There are two caveats with this computation:
// 1. Max output for the represented number is ~10^41, otherwise an intermediate value overflows. 10^41 is
// stored internally as a uint256 ~10^59.
// 2. Results that can't be represented exactly are truncated not rounded. E.g., 1.4 * 2e-18 = 2.8e-18, which
// would round to 3, but this computation produces the result 2.
// No need to use SafeMath because FP_SCALING_FACTOR != 0.
return Unsigned(a.rawValue.mul(b.rawValue) / FP_SCALING_FACTOR);
}
/**
* @notice Multiplies an `Unsigned` and an unscaled uint256, reverting on overflow.
* @dev This will "floor" the product.
* @param a a FixedPoint.
* @param b a uint256.
* @return the product of `a` and `b`.
*/
function mul(Unsigned memory a, uint256 b) internal pure returns (Unsigned memory) {
return Unsigned(a.rawValue.mul(b));
}
/**
* @notice Multiplies two `Unsigned`s and "ceil's" the product, reverting on overflow.
* @param a a FixedPoint.
* @param b a FixedPoint.
* @return the product of `a` and `b`.
*/
function mulCeil(Unsigned memory a, Unsigned memory b) internal pure returns (Unsigned memory) {
uint256 mulRaw = a.rawValue.mul(b.rawValue);
uint256 mulFloor = mulRaw / FP_SCALING_FACTOR;
uint256 mod = mulRaw.mod(FP_SCALING_FACTOR);
if (mod != 0) {
return Unsigned(mulFloor.add(1));
} else {
return Unsigned(mulFloor);
}
}
/**
* @notice Multiplies an `Unsigned` and an unscaled uint256 and "ceil's" the product, reverting on overflow.
* @param a a FixedPoint.
* @param b a FixedPoint.
* @return the product of `a` and `b`.
*/
function mulCeil(Unsigned memory a, uint256 b) internal pure returns (Unsigned memory) {
// Since b is an uint, there is no risk of truncation and we can just mul it normally
return Unsigned(a.rawValue.mul(b));
}
/**
* @notice Divides one `Unsigned` by an `Unsigned`, reverting on overflow or division by 0.
* @dev This will "floor" the quotient.
* @param a a FixedPoint numerator.
* @param b a FixedPoint denominator.
* @return the quotient of `a` divided by `b`.
*/
function div(Unsigned memory a, Unsigned memory b) internal pure returns (Unsigned memory) {
// There are two caveats with this computation:
// 1. Max value for the number dividend `a` represents is ~10^41, otherwise an intermediate value overflows.
// 10^41 is stored internally as a uint256 10^59.
// 2. Results that can't be represented exactly are truncated not rounded. E.g., 2 / 3 = 0.6 repeating, which
// would round to 0.666666666666666667, but this computation produces the result 0.666666666666666666.
return Unsigned(a.rawValue.mul(FP_SCALING_FACTOR).div(b.rawValue));
}
/**
* @notice Divides one `Unsigned` by an unscaled uint256, reverting on overflow or division by 0.
* @dev This will "floor" the quotient.
* @param a a FixedPoint numerator.
* @param b a uint256 denominator.
* @return the quotient of `a` divided by `b`.
*/
function div(Unsigned memory a, uint256 b) internal pure returns (Unsigned memory) {
return Unsigned(a.rawValue.div(b));
}
/**
* @notice Divides one unscaled uint256 by an `Unsigned`, reverting on overflow or division by 0.
* @dev This will "floor" the quotient.
* @param a a uint256 numerator.
* @param b a FixedPoint denominator.
* @return the quotient of `a` divided by `b`.
*/
function div(uint256 a, Unsigned memory b) internal pure returns (Unsigned memory) {
return div(fromUnscaledUint(a), b);
}
/**
* @notice Divides one `Unsigned` by an `Unsigned` and "ceil's" the quotient, reverting on overflow or division by 0.
* @param a a FixedPoint numerator.
* @param b a FixedPoint denominator.
* @return the quotient of `a` divided by `b`.
*/
function divCeil(Unsigned memory a, Unsigned memory b) internal pure returns (Unsigned memory) {
uint256 aScaled = a.rawValue.mul(FP_SCALING_FACTOR);
uint256 divFloor = aScaled.div(b.rawValue);
uint256 mod = aScaled.mod(b.rawValue);
if (mod != 0) {
return Unsigned(divFloor.add(1));
} else {
return Unsigned(divFloor);
}
}
/**
* @notice Divides one `Unsigned` by an unscaled uint256 and "ceil's" the quotient, reverting on overflow or division by 0.
* @param a a FixedPoint numerator.
* @param b a uint256 denominator.
* @return the quotient of `a` divided by `b`.
*/
function divCeil(Unsigned memory a, uint256 b) internal pure returns (Unsigned memory) {
// Because it is possible that a quotient gets truncated, we can't just call "Unsigned(a.rawValue.div(b))"
// similarly to mulCeil with a uint256 as the second parameter. Therefore we need to convert b into an Unsigned.
// This creates the possibility of overflow if b is very large.
return divCeil(a, fromUnscaledUint(b));
}
/**
* @notice Raises an `Unsigned` to the power of an unscaled uint256, reverting on overflow. E.g., `b=2` squares `a`.
* @dev This will "floor" the result.
* @param a a FixedPoint numerator.
* @param b a uint256 denominator.
* @return output is `a` to the power of `b`.
*/
function pow(Unsigned memory a, uint256 b) internal pure returns (Unsigned memory output) {
output = fromUnscaledUint(1);
for (uint256 i = 0; i < b; i = i.add(1)) {
output = mul(output, a);
}
}
// ------------------------------------------------- SIGNED -------------------------------------------------------------
// Supports 18 decimals. E.g., 1e18 represents "1", 5e17 represents "0.5".
// For signed values:
// This can represent a value up (or down) to +-(2^255 - 1)/10^18 = ~10^58. 10^58 will be stored internally as int256 10^76.
int256 private constant SFP_SCALING_FACTOR = 10**18;
struct Signed {
int256 rawValue;
}
function fromSigned(Signed memory a) internal pure returns (Unsigned memory) {
require(a.rawValue >= 0, "Negative value provided");
return Unsigned(uint256(a.rawValue));
}
function fromUnsigned(Unsigned memory a) internal pure returns (Signed memory) {
require(a.rawValue <= uint256(type(int256).max), "Unsigned too large");
return Signed(int256(a.rawValue));
}
/**
* @notice Constructs a `Signed` from an unscaled int, e.g., `b=5` gets stored internally as `5*(10**18)`.
* @param a int to convert into a FixedPoint.Signed.
* @return the converted FixedPoint.Signed.
*/
function fromUnscaledInt(int256 a) internal pure returns (Signed memory) {
return Signed(a.mul(SFP_SCALING_FACTOR));
}
/**
* @notice Whether `a` is equal to `b`.
* @param a a FixedPoint.Signed.
* @param b a int256.
* @return True if equal, or False.
*/
function isEqual(Signed memory a, int256 b) internal pure returns (bool) {
return a.rawValue == fromUnscaledInt(b).rawValue;
}
/**
* @notice Whether `a` is equal to `b`.
* @param a a FixedPoint.Signed.
* @param b a FixedPoint.Signed.
* @return True if equal, or False.
*/
function isEqual(Signed memory a, Signed memory b) internal pure returns (bool) {
return a.rawValue == b.rawValue;
}
/**
* @notice Whether `a` is greater than `b`.
* @param a a FixedPoint.Signed.
* @param b a FixedPoint.Signed.
* @return True if `a > b`, or False.
*/
function isGreaterThan(Signed memory a, Signed memory b) internal pure returns (bool) {
return a.rawValue > b.rawValue;
}
/**
* @notice Whether `a` is greater than `b`.
* @param a a FixedPoint.Signed.
* @param b an int256.
* @return True if `a > b`, or False.
*/
function isGreaterThan(Signed memory a, int256 b) internal pure returns (bool) {
return a.rawValue > fromUnscaledInt(b).rawValue;
}
/**
* @notice Whether `a` is greater than `b`.
* @param a an int256.
* @param b a FixedPoint.Signed.
* @return True if `a > b`, or False.
*/
function isGreaterThan(int256 a, Signed memory b) internal pure returns (bool) {
return fromUnscaledInt(a).rawValue > b.rawValue;
}
/**
* @notice Whether `a` is greater than or equal to `b`.
* @param a a FixedPoint.Signed.
* @param b a FixedPoint.Signed.
* @return True if `a >= b`, or False.
*/
function isGreaterThanOrEqual(Signed memory a, Signed memory b) internal pure returns (bool) {
return a.rawValue >= b.rawValue;
}
/**
* @notice Whether `a` is greater than or equal to `b`.
* @param a a FixedPoint.Signed.
* @param b an int256.
* @return True if `a >= b`, or False.
*/
function isGreaterThanOrEqual(Signed memory a, int256 b) internal pure returns (bool) {
return a.rawValue >= fromUnscaledInt(b).rawValue;
}
/**
* @notice Whether `a` is greater than or equal to `b`.
* @param a an int256.
* @param b a FixedPoint.Signed.
* @return True if `a >= b`, or False.
*/
function isGreaterThanOrEqual(int256 a, Signed memory b) internal pure returns (bool) {
return fromUnscaledInt(a).rawValue >= b.rawValue;
}
/**
* @notice Whether `a` is less than `b`.
* @param a a FixedPoint.Signed.
* @param b a FixedPoint.Signed.
* @return True if `a < b`, or False.
*/
function isLessThan(Signed memory a, Signed memory b) internal pure returns (bool) {
return a.rawValue < b.rawValue;
}
/**
* @notice Whether `a` is less than `b`.
* @param a a FixedPoint.Signed.
* @param b an int256.
* @return True if `a < b`, or False.
*/
function isLessThan(Signed memory a, int256 b) internal pure returns (bool) {
return a.rawValue < fromUnscaledInt(b).rawValue;
}
/**
* @notice Whether `a` is less than `b`.
* @param a an int256.
* @param b a FixedPoint.Signed.
* @return True if `a < b`, or False.
*/
function isLessThan(int256 a, Signed memory b) internal pure returns (bool) {
return fromUnscaledInt(a).rawValue < b.rawValue;
}
/**
* @notice Whether `a` is less than or equal to `b`.
* @param a a FixedPoint.Signed.
* @param b a FixedPoint.Signed.
* @return True if `a <= b`, or False.
*/
function isLessThanOrEqual(Signed memory a, Signed memory b) internal pure returns (bool) {
return a.rawValue <= b.rawValue;
}
/**
* @notice Whether `a` is less than or equal to `b`.
* @param a a FixedPoint.Signed.
* @param b an int256.
* @return True if `a <= b`, or False.
*/
function isLessThanOrEqual(Signed memory a, int256 b) internal pure returns (bool) {
return a.rawValue <= fromUnscaledInt(b).rawValue;
}
/**
* @notice Whether `a` is less than or equal to `b`.
* @param a an int256.
* @param b a FixedPoint.Signed.
* @return True if `a <= b`, or False.
*/
function isLessThanOrEqual(int256 a, Signed memory b) internal pure returns (bool) {
return fromUnscaledInt(a).rawValue <= b.rawValue;
}
/**
* @notice The minimum of `a` and `b`.
* @param a a FixedPoint.Signed.
* @param b a FixedPoint.Signed.
* @return the minimum of `a` and `b`.
*/
function min(Signed memory a, Signed memory b) internal pure returns (Signed memory) {
return a.rawValue < b.rawValue ? a : b;
}
/**
* @notice The maximum of `a` and `b`.
* @param a a FixedPoint.Signed.
* @param b a FixedPoint.Signed.
* @return the maximum of `a` and `b`.
*/
function max(Signed memory a, Signed memory b) internal pure returns (Signed memory) {
return a.rawValue > b.rawValue ? a : b;
}
/**
* @notice Adds two `Signed`s, reverting on overflow.
* @param a a FixedPoint.Signed.
* @param b a FixedPoint.Signed.
* @return the sum of `a` and `b`.
*/
function add(Signed memory a, Signed memory b) internal pure returns (Signed memory) {
return Signed(a.rawValue.add(b.rawValue));
}
/**
* @notice Adds an `Signed` to an unscaled int, reverting on overflow.
* @param a a FixedPoint.Signed.
* @param b an int256.
* @return the sum of `a` and `b`.
*/
function add(Signed memory a, int256 b) internal pure returns (Signed memory) {
return add(a, fromUnscaledInt(b));
}
/**
* @notice Subtracts two `Signed`s, reverting on overflow.
* @param a a FixedPoint.Signed.
* @param b a FixedPoint.Signed.
* @return the difference of `a` and `b`.
*/
function sub(Signed memory a, Signed memory b) internal pure returns (Signed memory) {
return Signed(a.rawValue.sub(b.rawValue));
}
/**
* @notice Subtracts an unscaled int256 from an `Signed`, reverting on overflow.
* @param a a FixedPoint.Signed.
* @param b an int256.
* @return the difference of `a` and `b`.
*/
function sub(Signed memory a, int256 b) internal pure returns (Signed memory) {
return sub(a, fromUnscaledInt(b));
}
/**
* @notice Subtracts an `Signed` from an unscaled int256, reverting on overflow.
* @param a an int256.
* @param b a FixedPoint.Signed.
* @return the difference of `a` and `b`.
*/
function sub(int256 a, Signed memory b) internal pure returns (Signed memory) {
return sub(fromUnscaledInt(a), b);
}
/**
* @notice Multiplies two `Signed`s, reverting on overflow.
* @dev This will "floor" the product.
* @param a a FixedPoint.Signed.
* @param b a FixedPoint.Signed.
* @return the product of `a` and `b`.
*/
function mul(Signed memory a, Signed memory b) internal pure returns (Signed memory) {
// There are two caveats with this computation:
// 1. Max output for the represented number is ~10^41, otherwise an intermediate value overflows. 10^41 is
// stored internally as an int256 ~10^59.
// 2. Results that can't be represented exactly are truncated not rounded. E.g., 1.4 * 2e-18 = 2.8e-18, which
// would round to 3, but this computation produces the result 2.
// No need to use SafeMath because SFP_SCALING_FACTOR != 0.
return Signed(a.rawValue.mul(b.rawValue) / SFP_SCALING_FACTOR);
}
/**
* @notice Multiplies an `Signed` and an unscaled int256, reverting on overflow.
* @dev This will "floor" the product.
* @param a a FixedPoint.Signed.
* @param b an int256.
* @return the product of `a` and `b`.
*/
function mul(Signed memory a, int256 b) internal pure returns (Signed memory) {
return Signed(a.rawValue.mul(b));
}
/**
* @notice Multiplies two `Signed`s and "ceil's" the product, reverting on overflow.
* @param a a FixedPoint.Signed.
* @param b a FixedPoint.Signed.
* @return the product of `a` and `b`.
*/
function mulAwayFromZero(Signed memory a, Signed memory b) internal pure returns (Signed memory) {
int256 mulRaw = a.rawValue.mul(b.rawValue);
int256 mulTowardsZero = mulRaw / SFP_SCALING_FACTOR;
// Manual mod because SignedSafeMath doesn't support it.
int256 mod = mulRaw % SFP_SCALING_FACTOR;
if (mod != 0) {
bool isResultPositive = isLessThan(a, 0) == isLessThan(b, 0);
int256 valueToAdd = isResultPositive ? int256(1) : int256(-1);
return Signed(mulTowardsZero.add(valueToAdd));
} else {
return Signed(mulTowardsZero);
}
}
/**
* @notice Multiplies an `Signed` and an unscaled int256 and "ceil's" the product, reverting on overflow.
* @param a a FixedPoint.Signed.
* @param b a FixedPoint.Signed.
* @return the product of `a` and `b`.
*/
function mulAwayFromZero(Signed memory a, int256 b) internal pure returns (Signed memory) {
// Since b is an int, there is no risk of truncation and we can just mul it normally
return Signed(a.rawValue.mul(b));
}
/**
* @notice Divides one `Signed` by an `Signed`, reverting on overflow or division by 0.
* @dev This will "floor" the quotient.
* @param a a FixedPoint numerator.
* @param b a FixedPoint denominator.
* @return the quotient of `a` divided by `b`.
*/
function div(Signed memory a, Signed memory b) internal pure returns (Signed memory) {
// There are two caveats with this computation:
// 1. Max value for the number dividend `a` represents is ~10^41, otherwise an intermediate value overflows.
// 10^41 is stored internally as an int256 10^59.
// 2. Results that can't be represented exactly are truncated not rounded. E.g., 2 / 3 = 0.6 repeating, which
// would round to 0.666666666666666667, but this computation produces the result 0.666666666666666666.
return Signed(a.rawValue.mul(SFP_SCALING_FACTOR).div(b.rawValue));
}
/**
* @notice Divides one `Signed` by an unscaled int256, reverting on overflow or division by 0.
* @dev This will "floor" the quotient.
* @param a a FixedPoint numerator.
* @param b an int256 denominator.
* @return the quotient of `a` divided by `b`.
*/
function div(Signed memory a, int256 b) internal pure returns (Signed memory) {
return Signed(a.rawValue.div(b));
}
/**
* @notice Divides one unscaled int256 by an `Signed`, reverting on overflow or division by 0.
* @dev This will "floor" the quotient.
* @param a an int256 numerator.
* @param b a FixedPoint denominator.
* @return the quotient of `a` divided by `b`.
*/
function div(int256 a, Signed memory b) internal pure returns (Signed memory) {
return div(fromUnscaledInt(a), b);
}
/**
* @notice Divides one `Signed` by an `Signed` and "ceil's" the quotient, reverting on overflow or division by 0.
* @param a a FixedPoint numerator.
* @param b a FixedPoint denominator.
* @return the quotient of `a` divided by `b`.
*/
function divAwayFromZero(Signed memory a, Signed memory b) internal pure returns (Signed memory) {
int256 aScaled = a.rawValue.mul(SFP_SCALING_FACTOR);
int256 divTowardsZero = aScaled.div(b.rawValue);
// Manual mod because SignedSafeMath doesn't support it.
int256 mod = aScaled % b.rawValue;
if (mod != 0) {
bool isResultPositive = isLessThan(a, 0) == isLessThan(b, 0);
int256 valueToAdd = isResultPositive ? int256(1) : int256(-1);
return Signed(divTowardsZero.add(valueToAdd));
} else {
return Signed(divTowardsZero);
}
}
/**
* @notice Divides one `Signed` by an unscaled int256 and "ceil's" the quotient, reverting on overflow or division by 0.
* @param a a FixedPoint numerator.
* @param b an int256 denominator.
* @return the quotient of `a` divided by `b`.
*/
function divAwayFromZero(Signed memory a, int256 b) internal pure returns (Signed memory) {
// Because it is possible that a quotient gets truncated, we can't just call "Signed(a.rawValue.div(b))"
// similarly to mulCeil with an int256 as the second parameter. Therefore we need to convert b into an Signed.
// This creates the possibility of overflow if b is very large.
return divAwayFromZero(a, fromUnscaledInt(b));
}
/**
* @notice Raises an `Signed` to the power of an unscaled uint256, reverting on overflow. E.g., `b=2` squares `a`.
* @dev This will "floor" the result.
* @param a a FixedPoint.Signed.
* @param b a uint256 (negative exponents are not allowed).
* @return output is `a` to the power of `b`.
*/
function pow(Signed memory a, uint256 b) internal pure returns (Signed memory output) {
output = fromUnscaledInt(1);
for (uint256 i = 0; i < b; i = i.add(1)) {
output = mul(output, a);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/math/SafeMath.sol)
pragma solidity ^0.8.0;
// CAUTION
// This version of SafeMath should only be used with Solidity 0.8 or later,
// because it relies on the compiler's built in overflow checks.
/**
* @dev Wrappers over Solidity's arithmetic operations.
*
* NOTE: `SafeMath` is generally not needed starting with Solidity 0.8, since the compiler
* now has built in overflow checking.
*/
library SafeMath {
/**
* @dev Returns the addition of two unsigned integers, with an overflow flag.
*
* _Available since v3.4._
*/
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 substraction of two unsigned integers, with an overflow flag.
*
* _Available since v3.4._
*/
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.
*
* _Available since v3.4._
*/
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.
*
* _Available since v3.4._
*/
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.
*
* _Available since v3.4._
*/
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 addition of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
*
* - Addition cannot overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
return a + b;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
*
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
return a - b;
}
/**
* @dev Returns the multiplication of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `*` operator.
*
* Requirements:
*
* - Multiplication cannot overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
return a * b;
}
/**
* @dev Returns the integer division of two unsigned integers, reverting on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator.
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
return a / b;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* reverting when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
return a % b;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting with custom message on
* overflow (when the result is negative).
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {trySub}.
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
*
* - Subtraction cannot overflow.
*/
function sub(
uint256 a,
uint256 b,
string memory errorMessage
) internal pure returns (uint256) {
unchecked {
require(b <= a, errorMessage);
return a - b;
}
}
/**
* @dev Returns the integer division of two unsigned integers, reverting with custom message on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function div(
uint256 a,
uint256 b,
string memory errorMessage
) internal pure returns (uint256) {
unchecked {
require(b > 0, errorMessage);
return a / b;
}
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* reverting with custom message when dividing by zero.
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {tryMod}.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function mod(
uint256 a,
uint256 b,
string memory errorMessage
) internal pure returns (uint256) {
unchecked {
require(b > 0, errorMessage);
return a % b;
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/math/SignedSafeMath.sol)
pragma solidity ^0.8.0;
/**
* @dev Wrappers over Solidity's arithmetic operations.
*
* NOTE: `SignedSafeMath` is no longer needed starting with Solidity 0.8. The compiler
* now has built in overflow checking.
*/
library SignedSafeMath {
/**
* @dev Returns the multiplication of two signed integers, reverting on
* overflow.
*
* Counterpart to Solidity's `*` operator.
*
* Requirements:
*
* - Multiplication cannot overflow.
*/
function mul(int256 a, int256 b) internal pure returns (int256) {
return a * b;
}
/**
* @dev Returns the integer division of two signed integers. Reverts on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator.
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function div(int256 a, int256 b) internal pure returns (int256) {
return a / b;
}
/**
* @dev Returns the subtraction of two signed integers, reverting on
* overflow.
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
*
* - Subtraction cannot overflow.
*/
function sub(int256 a, int256 b) internal pure returns (int256) {
return a - b;
}
/**
* @dev Returns the addition of two signed integers, reverting on
* overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
*
* - Addition cannot overflow.
*/
function add(int256 a, int256 b) internal pure returns (int256) {
return a + b;
}
}// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
/**
* @title Financial contract facing Oracle interface.
* @dev Interface used by financial contracts to interact with the Oracle. Voters will use a different interface.
*/
abstract contract OptimisticOracleInterface {
// Struct representing the state of a price request.
enum State {
Invalid, // Never requested.
Requested, // Requested, no other actions taken.
Proposed, // Proposed, but not expired or disputed yet.
Expired, // Proposed, not disputed, past liveness.
Disputed, // Disputed, but no DVM price returned yet.
Resolved, // Disputed and DVM price is available.
Settled // Final price has been set in the contract (can get here from Expired or Resolved).
}
// Struct representing a price request.
struct Request {
address proposer; // Address of the proposer.
address disputer; // Address of the disputer.
IERC20 currency; // ERC20 token used to pay rewards and fees.
bool settled; // True if the request is settled.
bool refundOnDispute; // True if the requester should be refunded their reward on dispute.
int256 proposedPrice; // Price that the proposer submitted.
int256 resolvedPrice; // Price resolved once the request is settled.
uint256 expirationTime; // Time at which the request auto-settles without a dispute.
uint256 reward; // Amount of the currency to pay to the proposer on settlement.
uint256 finalFee; // Final fee to pay to the Store upon request to the DVM.
uint256 bond; // Bond that the proposer and disputer must pay on top of the final fee.
uint256 customLiveness; // Custom liveness value set by the requester.
}
// This value must be <= the Voting contract's `ancillaryBytesLimit` value otherwise it is possible
// that a price can be requested to this contract successfully, but cannot be disputed because the DVM refuses
// to accept a price request made with ancillary data length over a certain size.
uint256 public constant ancillaryBytesLimit = 8192;
/**
* @notice Requests a new price.
* @param identifier price identifier being requested.
* @param timestamp timestamp of the price being requested.
* @param ancillaryData ancillary data representing additional args being passed with the price request.
* @param currency ERC20 token used for payment of rewards and fees. Must be approved for use with the DVM.
* @param reward reward offered to a successful proposer. Will be pulled from the caller. Note: this can be 0,
* which could make sense if the contract requests and proposes the value in the same call or
* provides its own reward system.
* @return totalBond default bond (final fee) + final fee that the proposer and disputer will be required to pay.
* This can be changed with a subsequent call to setBond().
*/
function requestPrice(
bytes32 identifier,
uint256 timestamp,
bytes memory ancillaryData,
IERC20 currency,
uint256 reward
) external virtual returns (uint256 totalBond);
/**
* @notice Set the proposal bond associated with a price request.
* @param identifier price identifier to identify the existing request.
* @param timestamp timestamp to identify the existing request.
* @param ancillaryData ancillary data of the price being requested.
* @param bond custom bond amount to set.
* @return totalBond new bond + final fee that the proposer and disputer will be required to pay. This can be
* changed again with a subsequent call to setBond().
*/
function setBond(
bytes32 identifier,
uint256 timestamp,
bytes memory ancillaryData,
uint256 bond
) external virtual returns (uint256 totalBond);
/**
* @notice Sets the request to refund the reward if the proposal is disputed. This can help to "hedge" the caller
* in the event of a dispute-caused delay. Note: in the event of a dispute, the winner still receives the other's
* bond, so there is still profit to be made even if the reward is refunded.
* @param identifier price identifier to identify the existing request.
* @param timestamp timestamp to identify the existing request.
* @param ancillaryData ancillary data of the price being requested.
*/
function setRefundOnDispute(
bytes32 identifier,
uint256 timestamp,
bytes memory ancillaryData
) external virtual;
/**
* @notice Sets a custom liveness value for the request. Liveness is the amount of time a proposal must wait before
* being auto-resolved.
* @param identifier price identifier to identify the existing request.
* @param timestamp timestamp to identify the existing request.
* @param ancillaryData ancillary data of the price being requested.
* @param customLiveness new custom liveness.
*/
function setCustomLiveness(
bytes32 identifier,
uint256 timestamp,
bytes memory ancillaryData,
uint256 customLiveness
) external virtual;
/**
* @notice Proposes a price value on another address' behalf. Note: this address will receive any rewards that come
* from this proposal. However, any bonds are pulled from the caller.
* @param proposer address to set as the proposer.
* @param requester sender of the initial price request.
* @param identifier price identifier to identify the existing request.
* @param timestamp timestamp to identify the existing request.
* @param ancillaryData ancillary data of the price being requested.
* @param proposedPrice price being proposed.
* @return totalBond the amount that's pulled from the caller's wallet as a bond. The bond will be returned to
* the proposer once settled if the proposal is correct.
*/
function proposePriceFor(
address proposer,
address requester,
bytes32 identifier,
uint256 timestamp,
bytes memory ancillaryData,
int256 proposedPrice
) public virtual returns (uint256 totalBond);
/**
* @notice Proposes a price value for an existing price request.
* @param requester sender of the initial price request.
* @param identifier price identifier to identify the existing request.
* @param timestamp timestamp to identify the existing request.
* @param ancillaryData ancillary data of the price being requested.
* @param proposedPrice price being proposed.
* @return totalBond the amount that's pulled from the proposer's wallet as a bond. The bond will be returned to
* the proposer once settled if the proposal is correct.
*/
function proposePrice(
address requester,
bytes32 identifier,
uint256 timestamp,
bytes memory ancillaryData,
int256 proposedPrice
) external virtual returns (uint256 totalBond);
/**
* @notice Disputes a price request with an active proposal on another address' behalf. Note: this address will
* receive any rewards that come from this dispute. However, any bonds are pulled from the caller.
* @param disputer address to set as the disputer.
* @param requester sender of the initial price request.
* @param identifier price identifier to identify the existing request.
* @param timestamp timestamp to identify the existing request.
* @param ancillaryData ancillary data of the price being requested.
* @return totalBond the amount that's pulled from the caller's wallet as a bond. The bond will be returned to
* the disputer once settled if the dispute was value (the proposal was incorrect).
*/
function disputePriceFor(
address disputer,
address requester,
bytes32 identifier,
uint256 timestamp,
bytes memory ancillaryData
) public virtual returns (uint256 totalBond);
/**
* @notice Disputes a price value for an existing price request with an active proposal.
* @param requester sender of the initial price request.
* @param identifier price identifier to identify the existing request.
* @param timestamp timestamp to identify the existing request.
* @param ancillaryData ancillary data of the price being requested.
* @return totalBond the amount that's pulled from the disputer's wallet as a bond. The bond will be returned to
* the disputer once settled if the dispute was valid (the proposal was incorrect).
*/
function disputePrice(
address requester,
bytes32 identifier,
uint256 timestamp,
bytes memory ancillaryData
) external virtual returns (uint256 totalBond);
/**
* @notice Retrieves a price that was previously requested by a caller. Reverts if the request is not settled
* or settleable. Note: this method is not view so that this call may actually settle the price request if it
* hasn't been settled.
* @param identifier price identifier to identify the existing request.
* @param timestamp timestamp to identify the existing request.
* @param ancillaryData ancillary data of the price being requested.
* @return resolved price.
*/
function settleAndGetPrice(
bytes32 identifier,
uint256 timestamp,
bytes memory ancillaryData
) external virtual returns (int256);
/**
* @notice Attempts to settle an outstanding price request. Will revert if it isn't settleable.
* @param requester sender of the initial price request.
* @param identifier price identifier to identify the existing request.
* @param timestamp timestamp to identify the existing request.
* @param ancillaryData ancillary data of the price being requested.
* @return payout the amount that the "winner" (proposer or disputer) receives on settlement. This amount includes
* the returned bonds as well as additional rewards.
*/
function settle(
address requester,
bytes32 identifier,
uint256 timestamp,
bytes memory ancillaryData
) external virtual returns (uint256 payout);
/**
* @notice Gets the current data structure containing all information about a price request.
* @param requester sender of the initial price request.
* @param identifier price identifier to identify the existing request.
* @param timestamp timestamp to identify the existing request.
* @param ancillaryData ancillary data of the price being requested.
* @return the Request data structure.
*/
function getRequest(
address requester,
bytes32 identifier,
uint256 timestamp,
bytes memory ancillaryData
) public view virtual returns (Request memory);
/**
* @notice Returns the state of a price request.
* @param requester sender of the initial price request.
* @param identifier price identifier to identify the existing request.
* @param timestamp timestamp to identify the existing request.
* @param ancillaryData ancillary data of the price being requested.
* @return the State enum value.
*/
function getState(
address requester,
bytes32 identifier,
uint256 timestamp,
bytes memory ancillaryData
) public view virtual returns (State);
/**
* @notice Checks if a given request has resolved or been settled (i.e the optimistic oracle has a price).
* @param requester sender of the initial price request.
* @param identifier price identifier to identify the existing request.
* @param timestamp timestamp to identify the existing request.
* @param ancillaryData ancillary data of the price being requested.
* @return true if price has resolved or settled, false otherwise.
*/
function hasPrice(
address requester,
bytes32 identifier,
uint256 timestamp,
bytes memory ancillaryData
) public view virtual returns (bool);
function stampAncillaryData(bytes memory ancillaryData, address requester)
public
view
virtual
returns (bytes memory);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
pragma solidity ^0.8.0;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
}{
"optimizer": {
"enabled": true,
"runs": 200
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"libraries": {
"contracts/HubPool.sol:HubPool": {
"MerkleLib": "0x7dB69eb9F52eD773E9b03f5068A1ea0275b2fD9d"
}
}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"contract LpTokenFactoryInterface","name":"_lpTokenFactory","type":"address"},{"internalType":"contract FinderInterface","name":"_finder","type":"address"},{"internalType":"contract WETH9","name":"_weth","type":"address"},{"internalType":"address","name":"_timer","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"newBondToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"newBondAmount","type":"uint256"}],"name":"BondSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"l2ChainId","type":"uint256"},{"indexed":false,"internalType":"address","name":"adapter","type":"address"},{"indexed":false,"internalType":"address","name":"spokePool","type":"address"}],"name":"CrossChainContractsSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"poolRebalanceRoot","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"relayerRefundRoot","type":"bytes32"},{"indexed":false,"internalType":"bytes32","name":"slowRelayRoot","type":"bytes32"},{"indexed":true,"internalType":"address","name":"proposer","type":"address"}],"name":"EmergencyRootBundleDeleted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"newIdentifier","type":"bytes32"}],"name":"IdentifierSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"l1Token","type":"address"},{"indexed":false,"internalType":"address","name":"lpToken","type":"address"}],"name":"L1TokenEnabledForLiquidityProvision","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"l1Token","type":"address"},{"indexed":false,"internalType":"address","name":"lpToken","type":"address"}],"name":"L2TokenDisabledForLiquidityProvision","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"l1Token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"lpTokensMinted","type":"uint256"},{"indexed":true,"internalType":"address","name":"liquidityProvider","type":"address"}],"name":"LiquidityAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"l1Token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"lpTokensBurnt","type":"uint256"},{"indexed":true,"internalType":"address","name":"liquidityProvider","type":"address"}],"name":"LiquidityRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newLiveness","type":"uint256"}],"name":"LivenessSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bool","name":"isPaused","type":"bool"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint32","name":"challengePeriodEndTimestamp","type":"uint32"},{"indexed":false,"internalType":"uint8","name":"poolRebalanceLeafCount","type":"uint8"},{"indexed":false,"internalType":"uint256[]","name":"bundleEvaluationBlockNumbers","type":"uint256[]"},{"indexed":true,"internalType":"bytes32","name":"poolRebalanceRoot","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"relayerRefundRoot","type":"bytes32"},{"indexed":false,"internalType":"bytes32","name":"slowRelayRoot","type":"bytes32"},{"indexed":true,"internalType":"address","name":"proposer","type":"address"}],"name":"ProposeRootBundle","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"newProtocolFeeCaptureAddress","type":"address"},{"indexed":true,"internalType":"uint256","name":"newProtocolFeeCapturePct","type":"uint256"}],"name":"ProtocolFeeCaptureSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"l1Token","type":"address"},{"indexed":true,"internalType":"uint256","name":"accumulatedFees","type":"uint256"}],"name":"ProtocolFeesCapturedClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"disputer","type":"address"},{"indexed":false,"internalType":"uint256","name":"requestTime","type":"uint256"}],"name":"RootBundleCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"disputer","type":"address"},{"indexed":false,"internalType":"uint256","name":"requestTime","type":"uint256"}],"name":"RootBundleDisputed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"groupIndex","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"leafId","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"chainId","type":"uint256"},{"indexed":false,"internalType":"address[]","name":"l1Tokens","type":"address[]"},{"indexed":false,"internalType":"uint256[]","name":"bundleLpFees","type":"uint256[]"},{"indexed":false,"internalType":"int256[]","name":"netSendAmounts","type":"int256[]"},{"indexed":false,"internalType":"int256[]","name":"runningBalances","type":"int256[]"},{"indexed":true,"internalType":"address","name":"caller","type":"address"}],"name":"RootBundleExecuted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"originChainId","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"destinationChainId","type":"uint256"},{"indexed":true,"internalType":"address","name":"originToken","type":"address"},{"indexed":false,"internalType":"bool","name":"depositsEnabled","type":"bool"}],"name":"SetEnableDepositRoute","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"destinationChainId","type":"uint256"},{"indexed":true,"internalType":"address","name":"l1Token","type":"address"},{"indexed":true,"internalType":"address","name":"destinationToken","type":"address"}],"name":"SetPoolRebalanceRoute","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"chainId","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"message","type":"bytes"}],"name":"SpokePoolAdminFunctionTriggered","type":"event"},{"stateMutability":"payable","type":"fallback"},{"inputs":[{"internalType":"address","name":"l1Token","type":"address"},{"internalType":"uint256","name":"l1TokenAmount","type":"uint256"}],"name":"addLiquidity","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"bondAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"bondToken","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"l1Token","type":"address"}],"name":"claimProtocolFeesCaptured","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"crossChainContracts","outputs":[{"internalType":"address","name":"adapter","type":"address"},{"internalType":"address","name":"spokePool","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"l1Token","type":"address"}],"name":"disableL1TokenForLiquidityProvision","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"disputeRootBundle","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"emergencyDeleteProposal","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"l1Token","type":"address"}],"name":"enableL1TokenForLiquidityProvision","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"l1Token","type":"address"}],"name":"exchangeRateCurrent","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"chainId","type":"uint256"},{"internalType":"uint256","name":"groupIndex","type":"uint256"},{"internalType":"uint256[]","name":"bundleLpFees","type":"uint256[]"},{"internalType":"int256[]","name":"netSendAmounts","type":"int256[]"},{"internalType":"int256[]","name":"runningBalances","type":"int256[]"},{"internalType":"uint8","name":"leafId","type":"uint8"},{"internalType":"address[]","name":"l1Tokens","type":"address[]"},{"internalType":"bytes32[]","name":"proof","type":"bytes32[]"}],"name":"executeRootBundle","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"finder","outputs":[{"internalType":"contract FinderInterface","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"l1Token","type":"address"},{"internalType":"int256","name":"haircutAmount","type":"int256"}],"name":"haircutReserves","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"identifier","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"l1Token","type":"address"}],"name":"liquidityUtilizationCurrent","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"l1Token","type":"address"},{"internalType":"uint256","name":"relayedAmount","type":"uint256"}],"name":"liquidityUtilizationPostRelay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"liveness","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"loadEthForL2Calls","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"lpFeeRatePerSecond","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lpTokenFactory","outputs":[{"internalType":"contract LpTokenFactoryInterface","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes[]","name":"data","type":"bytes[]"}],"name":"multicall","outputs":[{"internalType":"bytes[]","name":"results","type":"bytes[]"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"destinationChainId","type":"uint256"},{"internalType":"address","name":"l1Token","type":"address"}],"name":"poolRebalanceRoute","outputs":[{"internalType":"address","name":"destinationToken","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"pooledTokens","outputs":[{"internalType":"address","name":"lpToken","type":"address"},{"internalType":"bool","name":"isEnabled","type":"bool"},{"internalType":"uint32","name":"lastLpFeeUpdate","type":"uint32"},{"internalType":"int256","name":"utilizedReserves","type":"int256"},{"internalType":"uint256","name":"liquidReserves","type":"uint256"},{"internalType":"uint256","name":"undistributedLpFees","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"bundleEvaluationBlockNumbers","type":"uint256[]"},{"internalType":"uint8","name":"poolRebalanceLeafCount","type":"uint8"},{"internalType":"bytes32","name":"poolRebalanceRoot","type":"bytes32"},{"internalType":"bytes32","name":"relayerRefundRoot","type":"bytes32"},{"internalType":"bytes32","name":"slowRelayRoot","type":"bytes32"}],"name":"proposeRootBundle","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"protocolFeeCaptureAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"protocolFeeCapturePct","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"chainId","type":"uint256"},{"internalType":"bytes","name":"functionData","type":"bytes"}],"name":"relaySpokePoolAdminFunction","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"l1Token","type":"address"},{"internalType":"uint256","name":"lpTokenAmount","type":"uint256"},{"internalType":"bool","name":"sendEth","type":"bool"}],"name":"removeLiquidity","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"rootBundleProposal","outputs":[{"internalType":"bytes32","name":"poolRebalanceRoot","type":"bytes32"},{"internalType":"bytes32","name":"relayerRefundRoot","type":"bytes32"},{"internalType":"bytes32","name":"slowRelayRoot","type":"bytes32"},{"internalType":"uint256","name":"claimedBitMap","type":"uint256"},{"internalType":"address","name":"proposer","type":"address"},{"internalType":"uint8","name":"unclaimedPoolRebalanceLeafCount","type":"uint8"},{"internalType":"uint32","name":"challengePeriodEndTimestamp","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"newBondToken","type":"address"},{"internalType":"uint256","name":"newBondAmount","type":"uint256"}],"name":"setBond","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"l2ChainId","type":"uint256"},{"internalType":"address","name":"adapter","type":"address"},{"internalType":"address","name":"spokePool","type":"address"}],"name":"setCrossChainContracts","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"time","type":"uint256"}],"name":"setCurrentTime","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"originChainId","type":"uint256"},{"internalType":"uint256","name":"destinationChainId","type":"uint256"},{"internalType":"address","name":"originToken","type":"address"},{"internalType":"bool","name":"depositsEnabled","type":"bool"}],"name":"setDepositRoute","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"newIdentifier","type":"bytes32"}],"name":"setIdentifier","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"newLiveness","type":"uint32"}],"name":"setLiveness","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"pause","type":"bool"}],"name":"setPaused","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"destinationChainId","type":"uint256"},{"internalType":"address","name":"l1Token","type":"address"},{"internalType":"address","name":"destinationToken","type":"address"}],"name":"setPoolRebalanceRoute","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newProtocolFeeCaptureAddress","type":"address"},{"internalType":"uint256","name":"newProtocolFeeCapturePct","type":"uint256"}],"name":"setProtocolFeeCapture","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"l1Token","type":"address"}],"name":"sync","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"timerAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"unclaimedAccumulatedProtocolFees","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"weth","outputs":[{"internalType":"contract WETH9","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]Contract Creation Code
60e0604052600c805463ffffffff60a01b191660e160a51b1790557f49535f4143524f53535f56325f42554e444c455f56414c494400000000000000600d5565015d3ef79800600e553480156200005557600080fd5b5060405162004ea538038062004ea5833981016040819052620000789162000153565b600080546001600160a81b0319166001600160a01b03831617600160a01b179055620000ab620000a53390565b620000e8565b506001600160a01b0392831660a05290821660c0528116608052600154600b8054610100600160a81b0319169190921661010002179055620001bb565b600180546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b6001600160a01b03811681146200015057600080fd5b50565b600080600080608085870312156200016a57600080fd5b845162000177816200013a565b60208601519094506200018a816200013a565b60408601519093506200019d816200013a565b6060860151909250620001b0816200013a565b939692955090935050565b60805160a05160c051614c74620002316000396000818161081d01528181610c400152818161190501528181612fdd01526131d30152600081816106df015261279b015260008181610544015281816109e501528181610ec10152818161102a01528181611b460152611cac0152614c746000f3fe60806040526004361061028a5760003560e01c80635c975abb1161015a578063a5841194116100c1578063dd70e5e81161007a578063dd70e5e81461087f578063e0f339e31461089f578063e40064d7146108bf578063e460e35c146108ec578063f0056a7d1461090c578063f2fde38b146109b157610299565b8063a5841194146107ab578063ac9650d8146107cb578063b60c2d7d146107eb578063b9a3c84c1461080b578063c28f43921461083f578063cd9499951461085f57610299565b80637998a1c4116101135780637998a1c41461070157806380c09a821461071757806380f323a7146107375780638bda0c001461074d5780638da5cb5b1461076d578063a16fd6e91461078b57610299565b80635c975abb14610640578063625997c01461066a57806369b62502146102975780636ad0690a1461067f578063715018a6146106b857806376ec08dd146106cd57610299565b806322f8e566116101fe57806333dc09ca116101b757806333dc09ca146105125780633fc8cef3146105325780634144fd61146105665780634f7473ff146105f7578063566887001461060d57806356864f381461062057610299565b806322f8e56614610458578063240f475f1461047857806326205d801461049d57806329cb924d146104bd5780632d0f6f84146104d25780632d32d557146104f257610299565b80630ee28a88116102505780630ee28a881461039557806310b99527146103b557806311cfc159146103d557806316c38b3c146103eb5780631c39c38d1461040b57806322395aaa1461044357610299565b8062660b53146102a1578062c99206146102c1578063084d0513146102e157806309474ae2146103145780630c501af91461037557610299565b36610299576102976109d1565b005b6102976109d1565b3480156102ad57600080fd5b506102976102bc366004613f19565b610a5a565b3480156102cd57600080fd5b506102976102dc366004613f45565b610bae565b3480156102ed57600080fd5b506103016102fc366004613f19565b610daf565b6040519081526020015b60405180910390f35b34801561032057600080fd5b5061035561032f366004613f45565b600960205260009081526040902080546001909101546001600160a01b03918216911682565b604080516001600160a01b0393841681529290911660208301520161030b565b34801561038157600080fd5b50610297610390366004613f5e565b610ddb565b3480156103a157600080fd5b506102976103b0366004613f89565b610e82565b3480156103c157600080fd5b506102976103d0366004613fcb565b611105565b3480156103e157600080fd5b50610301600e5481565b3480156103f757600080fd5b50610297610406366004614002565b6111b3565b34801561041757600080fd5b5060005461042b906001600160a01b031681565b6040516001600160a01b03909116815260200161030b565b34801561044f57600080fd5b5061029761122f565b34801561046457600080fd5b50610297610473366004613f45565b611610565b34801561048457600080fd5b50600b5461042b9061010090046001600160a01b031681565b3480156104a957600080fd5b506102976104b8366004613f19565b61166b565b3480156104c957600080fd5b506103016116de565b3480156104de57600080fd5b506102976104ed366004613f5e565b61176f565b3480156104fe57600080fd5b5061042b61050d36600461401f565b6117f2565b34801561051e57600080fd5b5061029761052d366004613f19565b611823565b34801561053e57600080fd5b5061042b7f000000000000000000000000000000000000000000000000000000000000000081565b34801561057257600080fd5b506002546003546004546005546006546105b094939291906001600160a01b03811690600160a01b810460ff1690600160a81b900463ffffffff1687565b6040805197885260208801969096529486019390935260608501919091526001600160a01b0316608084015260ff1660a083015263ffffffff1660c082015260e00161030b565b34801561060357600080fd5b50610301600f5481565b61029761061b366004613f19565b611aa4565b34801561062c57600080fd5b5061029761063b36600461404f565b611da3565b34801561064c57600080fd5b50600b5461065a9060ff1681565b604051901515815260200161030b565b34801561067657600080fd5b50610297611e8b565b34801561068b57600080fd5b50600c546106a390600160a01b900463ffffffff1681565b60405163ffffffff909116815260200161030b565b3480156106c457600080fd5b50610297611fcb565b3480156106d957600080fd5b5061042b7f000000000000000000000000000000000000000000000000000000000000000081565b34801561070d57600080fd5b50610301600d5481565b34801561072357600080fd5b50610297610732366004614230565b611fff565b34801561074357600080fd5b5061030160105481565b34801561075957600080fd5b5061029761076836600461432f565b612385565b34801561077957600080fd5b506001546001600160a01b031661042b565b34801561079757600080fd5b506103016107a6366004613f5e565b612530565b3480156107b757600080fd5b506102976107c6366004613f5e565b61255c565b6107de6107d936600461439f565b61257d565b60405161030b9190614439565b3480156107f757600080fd5b50610297610806366004613f5e565b612723565b34801561081757600080fd5b5061042b7f000000000000000000000000000000000000000000000000000000000000000081565b34801561084b57600080fd5b50600c5461042b906001600160a01b031681565b34801561086b57600080fd5b5061029761087a36600461449b565b6128e2565b34801561088b57600080fd5b5061029761089a3660046144e9565b6129bc565b3480156108ab57600080fd5b506103016108ba366004613f5e565b612a08565b3480156108cb57600080fd5b506103016108da366004613f5e565b600a6020526000908152604090205481565b3480156108f857600080fd5b50610297610907366004613fcb565b612a23565b34801561091857600080fd5b5061096f610927366004613f5e565b60076020526000908152604090208054600182015460028301546003909301546001600160a01b03831693600160a01b840460ff1693600160a81b900463ffffffff16929186565b604080516001600160a01b039097168752941515602087015263ffffffff909316938501939093526060840152608083019190915260a082015260c00161030b565b3480156109bd57600080fd5b506102976109cc366004613f5e565b612afc565b600054600160a01b900460ff1615610a58577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d0e30db0346040518263ffffffff1660e01b81526004016000604051808303818588803b158015610a3e57600080fd5b505af1158015610a52573d6000803e3d6000fd5b50505050505b565b6001546001600160a01b03163314610a8d5760405162461bcd60e51b8152600401610a849061456e565b60405180910390fd5b610a95612b94565b610a9d612bed565b670de0b6b3a7640000811115610af55760405162461bcd60e51b815260206004820152601960248201527f4261642070726f746f636f6c46656543617074757265506374000000000000006044820152606401610a84565b6001600160a01b038216610b4b5760405162461bcd60e51b815260206004820152601d60248201527f4261642070726f746f636f6c46656543617074757265416464726573730000006044820152606401610a84565b600b8054610100600160a81b0319166101006001600160a01b03851690810291909117909155600f8290556040518291907fc1993b89fd79a19ece7beb067ddc8534ca26d29c0ff94ea2f53b4a508d1eedc990600090a3610baa612bfc565b5050565b6001546001600160a01b03163314610bd85760405162461bcd60e51b8152600401610a849061456e565b600654600160a01b900460ff1615610c025760405162461bcd60e51b8152600401610a84906145a3565b610c0a612b94565b610c12612bed565b6040516302abf57960e61b8152721259195b9d1a599a595c95da1a5d195b1a5cdd606a1b60048201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063aafd5e4090602401602060405180830381865afa158015610c8f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610cb391906145da565b6040516390978d1b60e01b8152600481018490529091506001600160a01b038216906390978d1b90602401602060405180830381865afa158015610cfb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d1f91906145f7565b610d6b5760405162461bcd60e51b815260206004820152601860248201527f4964656e746966696572206e6f7420737570706f7274656400000000000000006044820152606401610a84565b600d8290556040518281527ff45367c278fcceff23d601ce4bdd191e5bd61687ff9f29dc7276a08fe54c0c5d9060200160405180910390a150610dac612bfc565b50565b6000610db9612b94565b610dc1612bed565b610dcb8383612c11565b9050610dd5612bfc565b92915050565b6001546001600160a01b03163314610e055760405162461bcd60e51b8152600401610a849061456e565b610e0d612b94565b610e15612bed565b6001600160a01b03818116600081815260076020908152604091829020805460ff60a01b1981169091558251938452909316928201929092527fac111b3b527b307393c94d98f26140effb71411054466818be97912d2d65f77691015b60405180910390a1610dac612bfc565b610e8a612b94565b610e92612bed565b600b5460ff1615610eb55760405162461bcd60e51b8152600401610a8490614614565b826001600160a01b03167f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03161480610ef3575080155b610f2f5760405162461bcd60e51b815260206004820152600d60248201526c086c2dce840e6cadcc840cae8d609b1b6044820152606401610a84565b6000670de0b6b3a7640000610f4385612d09565b610f4d9085614656565b610f57919061468b565b6001600160a01b038581166000908152600760205260409081902054905163079cc67960e41b81523360048201526024810187905292935016906379cc6790906044016020604051808303816000875af1158015610fb9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fdd91906145f7565b506001600160a01b0384166000908152600760205260408120600201805483929061100990849061469f565b9091555050811561109d57604051632e1a7d4d60e01b8152600481018290527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690632e1a7d4d90602401600060405180830381600087803b15801561107657600080fd5b505af115801561108a573d6000803e3d6000fd5b505050506110983382612e04565b6110b1565b6110b16001600160a01b0385163383612f1d565b604080518281526020810185905233916001600160a01b038716917fcda1185f28599e6bd14ab8a68b3c30a11e1dce4256b5e67e94dd3fd846a6c589910160405180910390a350611100612bfc565b505050565b6001546001600160a01b0316331461112f5760405162461bcd60e51b8152600401610a849061456e565b611137612b94565b61113f612bed565b806008600061114e8587612f80565b815260208101919091526040908101600090812080546001600160a01b0319166001600160a01b0394851617905590518383169285169186917f234e7af08f77827792cc909447f27d2e6a3e2d839b04e26b50b71704a131c8a89190a4611100612bfc565b6001546001600160a01b031633146111dd5760405162461bcd60e51b8152600401610a849061456e565b6111e5612b94565b6111ed612bed565b600b805460ff19168215159081179091556040517f0e2fb031ee032dc02d8011dc50b816eb450cf856abd8261680dac74f72165bd290600090a2610dac612bfc565b611237612b94565b61123f612bed565b60006112496116de565b60065490915063ffffffff600160a81b909104811690821611156112af5760405162461bcd60e51b815260206004820152601760248201527f5265717565737420706173736564206c6976656e6573730000000000000000006044820152606401610a84565b60006112b9612fbd565b905060105481106112d3576112cc6130c3565b50506115e9565b60006112dd6131a2565b601054600c549192506112fb916001600160a01b0316908390613246565b806001600160a01b031663af355d1e600d5485600c60009054906101000a90046001600160a01b0316600087601054611334919061469f565b600c5460065460405160e089901b6001600160e01b0319168152600481019790975263ffffffff9586166024880152610120604488015260006101248801526001600160a01b039485166064880152608487019390935260a4860191909152600160a01b900490921660c48401521660e4820152670de0b6b3a7640000610104820152610144016020604051808303816000875af19250505080156113f6575060408051601f3d908101601f191682019092526113f3918101906146b6565b60015b61140a576114026130c3565b5050506115e9565b600c54611422906001600160a01b03168360006132f8565b5060408051610160810182526006546001600160a01b039081168252600060208301819052600c549182169383019390935260608201839052670de0b6b3a7640000608083015260a0820183905260c082019061148c90600160a01b900463ffffffff16876146cf565b63ffffffff16815260200160008152602001848152602001846010546114b2919061469f565b8152600c5463ffffffff600160a01b820481166020909301929092526000600281905560038190556004819055600555600680546001600160c81b0319169055601054929350611512926001600160a01b03909116913391309161340d16565b601054600c5461152f916001600160a01b03909116908490613246565b600d5460405163139c641960e31b81526001600160a01b03841691639ce320c891611565919088908690339030906004016146f7565b6020604051808303816000875af1158015611584573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115a891906146b6565b5060405163ffffffff8516815233907f15951cb2ef6993bc23a55912e7d0bcac13e4797c432aaa334816aed6914a7a909060200160405180910390a2505050505b6116086115f46131a2565b600c546001600160a01b03169060006132f8565b610a58612bfc565b6000546001600160a01b031661162557600080fd5b60005460405163117c72b360e11b8152600481018390526001600160a01b03909116906322f8e56690602401600060405180830381600087803b158015610a3e57600080fd5b6001546001600160a01b031633146116955760405162461bcd60e51b8152600401610a849061456e565b61169d612b94565b6116a5612bed565b6001600160a01b038216600090815260076020526040812060010180548392906116d09084906147ea565b90915550610baa9050612bfc565b600080546001600160a01b03161561176a5760008054906101000a90046001600160a01b03166001600160a01b03166329cb924d6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611741573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061176591906146b6565b905090565b504290565b611777612b94565b61177f612bed565b6001600160a01b038082166000818152600a602052604081208054919055600b5490926117b3929161010090041683612f1d565b60405181906001600160a01b038416907f74740239d7d696c84422b720e125e1f47c4138c66d1f4d2a48e99f4197cdb79c90600090a350610dac612bfc565b6000600860006118028486612f80565b81526020810191909152604001600020546001600160a01b03169392505050565b6001546001600160a01b0316331461184d5760405162461bcd60e51b8152600401610a849061456e565b600654600160a01b900460ff16156118775760405162461bcd60e51b8152600401610a84906145a3565b61187f612b94565b611887612bed565b806000036118d75760405162461bcd60e51b815260206004820152601760248201527f626f6e6420657175616c20746f2066696e616c206665650000000000000000006044820152606401610a84565b6040516302abf57960e61b81527210dbdb1b185d195c985b15da1a5d195b1a5cdd606a1b60048201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063aafd5e4090602401602060405180830381865afa158015611954573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061197891906145da565b604051631d1d5b3960e11b81526001600160a01b03858116600483015291925090821690633a3ab67290602401602060405180830381865afa1580156119c2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119e691906145f7565b611a255760405162461bcd60e51b815260206004820152601060248201526f139bdd081bdb881dda1a5d195b1a5cdd60821b6044820152606401610a84565b600c80546001600160a01b0319166001600160a01b0385161790556000611a4a612fbd565b611a549084614829565b60108190556040518181529091506001600160a01b038516907fbfa9a96010167e98ce8c004f718932cbbfd33a58d681c752e693be7d457a1b3b9060200160405180910390a25050610baa612bfc565b611aac612b94565b611ab4612bed565b600b5460ff1615611ad75760405162461bcd60e51b8152600401610a8490614614565b6001600160a01b038216600090815260076020526040902054600160a01b900460ff16611b3a5760405162461bcd60e51b8152602060048201526011602482015270151bdad95b881b9bdd08195b98589b1959607a1b6044820152606401610a84565b816001600160a01b03167f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316148015611b7a57508034145b80611b83575034155b611bbf5760405162461bcd60e51b815260206004820152600d60248201526c426164206d73672e76616c756560981b6044820152606401610a84565b6000611bca83612d09565b611bdc83670de0b6b3a7640000614656565b611be6919061468b565b6001600160a01b038416600090815260076020526040812060020180549293508492909190611c16908490614829565b90915550506001600160a01b03838116600090815260076020526040908190205490516340c10f1960e01b8152336004820152602481018490529116906340c10f19906044016020604051808303816000875af1158015611c7b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c9f91906145f7565b50826001600160a01b03167f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316148015611ce15750600034115b15611d3f57826001600160a01b031663d0e30db0346040518263ffffffff1660e01b81526004016000604051808303818588803b158015611d2157600080fd5b505af1158015611d35573d6000803e3d6000fd5b5050505050611d54565b611d546001600160a01b03841633308561340d565b604080518381526020810183905233916001600160a01b038616917f3c69701a61c79a92ef9692903aaa0068bce8771361ecb09547391e4fb4df8537910160405180910390a350610baa612bfc565b611dab612b94565b611db3612bed565b6001546001600160a01b03163314611ddd5760405162461bcd60e51b8152600401610a849061456e565b6040516001600160a01b0383166024820152604481018490528115156064820152611e3690859060840160408051601f198184030181529190526020810180516001600160e01b031663272751c760e01b179052613445565b816001600160a01b031683857fb7d00a563842efb2c121a0eb02b7bb7ba1a34625bbc3d65057f1f0dbec0ec2a184604051611e75911515815260200190565b60405180910390a4611e85612bfc565b50505050565b6001546001600160a01b03163314611eb55760405162461bcd60e51b8152600401610a849061456e565b611ebd612b94565b611ec5612bed565b6040805160e0810182526002805482526003805460208401526004805494840194909452600580546060850152600680546001600160a01b038116608087015260ff600160a01b82041660a0870181905263ffffffff600160a81b83041660c088015260009586905593859055958490559290556001600160c81b031990931690559015611f6d576080810151601054600c54611f6d926001600160a01b0390911691612f1d565b80608001516001600160a01b0316816020015182600001517f993cba33f9b140c9ce20ba10d7eda92128d5beb6df856f064916108a11647a738460400151604051611fba91815260200190565b60405180910390a450610a58612bfc565b6001546001600160a01b03163314611ff55760405162461bcd60e51b8152600401610a849061456e565b610a586000613549565b612007612b94565b61200f612bed565b600b5460ff16156120325760405162461bcd60e51b8152600401610a8490614614565b600654600160a81b900463ffffffff1661204a6116de565b1161208d5760405162461bcd60e51b81526020600482015260136024820152724e6f7420706173736564206c6976656e65737360681b6044820152606401610a84565b600554600160ff86161b908116036120d95760405162461bcd60e51b815260206004820152600f60248201526e105b1c9958591e4818db185a5b5959608a1b6044820152606401610a84565b6121516002600001546040518060e001604052808c81526020018a81526020018981526020018881526020018b81526020018760ff1681526020018681525084848080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525061359b92505050565b6121895760405162461bcd60e51b81526020600482015260096024820152682130b210283937b7b360b91b6044820152606401610a84565b6000806121958b6135d8565b915091506121a8600260030154876136a4565b600555600680546014906121c590600160a01b900460ff16614841565b91906101000a81548160ff021916908360ff1602179055506121eb82828d888c8e6136c7565b896000036122ed57600354600454604051602481019290925260448201526000906001600160a01b03841690839060640160408051601f198184030181529181526020820180516001600160e01b031663124e93e160e21b1790525161225592919060240161485e565b60408051601f198184030181529181526020820180516001600160e01b0316637375c56f60e11b1790525161228a9190614882565b600060405180830381855af49150503d80600081146122c5576040519150601f19603f3d011682016040523d82523d6000602084013e6122ca565b606091505b50509050806122eb5760405162461bcd60e51b8152600401610a849061489e565b505b600654600160a01b900460ff1660000361232257600654601054600c54612322926001600160a01b0391821692911690612f1d565b336001600160a01b03168b8760ff167ff652dd63b1aedbf9e740f3152fb67b0d94d069cf1182811ebd88921850d935678d898e8e8e60405161236895949392919061493f565b60405180910390a4505061237a612bfc565b505050505050505050565b61238d612b94565b612395612bed565b600654600160a01b900460ff16156123bf5760405162461bcd60e51b8152600401610a84906145a3565b600b5460ff16156123e25760405162461bcd60e51b8152600401610a8490614614565b60008460ff16116124355760405162461bcd60e51b815260206004820181905260248201527f42756e646c65206d7573742068617665206174206c656173742031206c6561666044820152606401610a84565b600c54600090600160a01b900463ffffffff166124506116de565b61245a91906146cf565b60006005556006805460028790556003869055600485905560ff8816600160a01b0263ffffffff808516600160a81b0260ff60a01b19166001600160c81b031990931692909217176001600160a01b03191633908117909255601054600c549394506124d4936001600160a01b0316929130919061340d16565b336001600160a01b031683857f3185fa6fac8e91dc65e7424a8081c73353151d2715bddb71db0982c1fe4c0fd484898c8c8960405161251795949392919061499e565b60405180910390a450612528612bfc565b505050505050565b600061253a612b94565b612542612bed565b61254d826000612c11565b9050612557612bfc565b919050565b612564612b94565b61256c612bed565b61257581613958565b610dac612bfc565b606034156125cd5760405162461bcd60e51b815260206004820152601b60248201527f4f6e6c79206d756c746963616c6c207769746820302076616c756500000000006044820152606401610a84565b8167ffffffffffffffff8111156125e6576125e6614099565b60405190808252806020026020018201604052801561261957816020015b60608152602001906001900390816126045790505b50905060005b8281101561271c576000803086868581811061263d5761263d6149fc565b905060200281019061264f9190614a12565b60405161265d929190614a59565b600060405180830381855af49150503d8060008114612698576040519150601f19603f3d011682016040523d82523d6000602084013e61269d565b606091505b5091509150816126e9576044815110156126b657600080fd5b600481019050808060200190518101906126d09190614a69565b60405162461bcd60e51b8152600401610a849190614ad7565b808484815181106126fc576126fc6149fc565b60200260200101819052505050808061271490614aea565b91505061261f565b5092915050565b6001546001600160a01b0316331461274d5760405162461bcd60e51b8152600401610a849061456e565b612755612b94565b61275d612bed565b6001600160a01b038181166000908152600760205260409020541661287b57604051637e178db760e11b81526001600160a01b0382811660048301527f0000000000000000000000000000000000000000000000000000000000000000169063fc2f1b6e906024016020604051808303816000875af11580156127e4573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061280891906145da565b6001600160a01b03828116600090815260076020526040902080546001600160a01b0319169290911691909117905561283f6116de565b6001600160a01b0382166000908152600760205260409020805463ffffffff92909216600160a81b0263ffffffff60a81b199092169190911790555b6001600160a01b038181166000818152600760209081526040918290208054600160a01b60ff60a01b198216179091558251938452909316928201929092527f04e291c80180d65a57b5bf1bed775777ec0d6f283ef34bcf130712714d8bb7f79101610e72565b6001546001600160a01b0316331461290c5760405162461bcd60e51b8152600401610a849061456e565b612914612b94565b61291c612bed565b6102588163ffffffff16116129685760405162461bcd60e51b8152602060048201526012602482015271131a5d995b995cdcc81d1bdbc81cda1bdc9d60721b6044820152606401610a84565b600c805463ffffffff60a01b1916600160a01b63ffffffff8416908102919091179091556040519081527f04dd1d84d387f404568a7954b5e398518bdd716e1a8f4a790be9a1a225ad934790602001610e72565b6001546001600160a01b031633146129e65760405162461bcd60e51b8152600401610a849061456e565b6129ee612b94565b6129f6612bed565b612a008282613445565b610baa612bfc565b6000612a12612b94565b612a1a612bed565b61254d82612d09565b6001546001600160a01b03163314612a4d5760405162461bcd60e51b8152600401610a849061456e565b612a55612b94565b612a5d612bed565b6040805180820182526001600160a01b03848116808352848216602080850182815260008a815260098352879020955186549086166001600160a01b031991821617875590516001909601805496909516951694909417909255835187815292830152918101919091527f36050d958750e6ac3aa674ac7bbe8d0ae6a2f7d4b808e8c2c42c1f22fc9fc4bb9060600160405180910390a1611100612bfc565b6001546001600160a01b03163314612b265760405162461bcd60e51b8152600401610a849061456e565b6001600160a01b038116612b8b5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401610a84565b610dac81613549565b600054600160a01b900460ff16610a585760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610a84565b6000805460ff60a01b19169055565b6000805460ff60a01b1916600160a01b179055565b6000612c1c83613958565b6001600160a01b038381166000908152600760209081526040808320815160c08101835281549586168152600160a01b860460ff16151593810193909352600160a81b90940463ffffffff16908201526001830154606082018190526002840154608083015260039093015460a0820152918112612c9b576000612ca1565b81606001515b90506000612caf8286614829565b90506000828460800151612cc39190614829565b905080600003612ce157670de0b6b3a7640000945050505050610dd5565b80612cf483670de0b6b3a7640000614656565b612cfe919061468b565b979650505050505050565b6001600160a01b038082166000908152600760209081526040808320805482516318160ddd60e01b8152925194959194869491909216926318160ddd92600480830193928290030181865afa158015612d66573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d8a91906146b6565b905080600003612da55750670de0b6b3a76400009392505050565b612dae82613aa7565b612db784613958565b6000826003015483600101548460020154612dd29190614b03565b612ddc91906147ea565b905081612df182670de0b6b3a7640000614656565b612dfb919061468b565b95945050505050565b80471015612e545760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a20696e73756666696369656e742062616c616e63650000006044820152606401610a84565b6000826001600160a01b03168260405160006040518083038185875af1925050503d8060008114612ea1576040519150601f19603f3d011682016040523d82523d6000602084013e612ea6565b606091505b50509050806111005760405162461bcd60e51b815260206004820152603a60248201527f416464726573733a20756e61626c6520746f2073656e642076616c75652c207260448201527f6563697069656e74206d617920686176652072657665727465640000000000006064820152608401610a84565b6040516001600160a01b03831660248201526044810182905261110090849063a9059cbb60e01b906064015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152613b0f565b604080516001600160a01b038416602082015290810182905260009060600160405160208183030381529060405280519060200120905092915050565b6040516302abf57960e61b81526453746f726560d81b60048201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063aafd5e4090602401602060405180830381865afa15801561302c573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061305091906145da565b600c54604051635b97aadd60e01b81526001600160a01b039182166004820152911690635b97aadd90602401602060405180830381865afa158015613099573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130bd9190614b44565b51919050565b600c5460065460105460405163a9059cbb60e01b81526001600160a01b039283166004820152602481019190915291169063a9059cbb906044016020604051808303816000875af115801561311c573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061314091906145f7565b506000600281905560038190556004819055600555600680546001600160c81b0319169055337f0cfbbf45ab7f5225663454de7117b1b0ed5a7c133b61f54ccf367dcf8b6d4d5961318f6116de565b60405190815260200160405180910390a2565b6040516302abf57960e61b815275536b696e6e794f7074696d69737469634f7261636c6560501b60048201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063aafd5e4090602401602060405180830381865afa158015613222573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061176591906145da565b604051636eb1769f60e11b81523060048201526001600160a01b038381166024830152600091839186169063dd62ed3e90604401602060405180830381865afa158015613297573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132bb91906146b6565b6132c59190614829565b6040516001600160a01b038516602482015260448101829052909150611e8590859063095ea7b360e01b90606401612f49565b8015806133725750604051636eb1769f60e11b81523060048201526001600160a01b03838116602483015284169063dd62ed3e90604401602060405180830381865afa15801561334c573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061337091906146b6565b155b6133dd5760405162461bcd60e51b815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527520746f206e6f6e2d7a65726f20616c6c6f77616e636560501b6064820152608401610a84565b6040516001600160a01b03831660248201526044810182905261110090849063095ea7b360e01b90606401612f49565b6040516001600160a01b0380851660248301528316604482015260648101829052611e859085906323b872dd60e01b90608401612f49565b600080613451846135d8565b915091506000826001600160a01b0316828560405160240161347492919061485e565b60408051601f198184030181529181526020820180516001600160e01b0316637375c56f60e11b179052516134a99190614882565b600060405180830381855af49150503d80600081146134e4576040519150601f19603f3d011682016040523d82523d6000602084013e6134e9565b606091505b505090508061350a5760405162461bcd60e51b8152600401610a849061489e565b847f218987b934c2f6bc596136829fbf43a5fef4d6fafce41f3f6254d9a870c2deec8560405161353a9190614ad7565b60405180910390a25050505050565b600180546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b60006135ce8285856040516020016135b39190614b86565b60405160208183030381529060405280519060200120613be1565b90505b9392505050565b600081815260096020526040902080546001909101546001600160a01b039182169116806136485760405162461bcd60e51b815260206004820152601960248201527f53706f6b65506f6f6c206e6f7420696e697469616c697a6564000000000000006044820152606401610a84565b6001600160a01b0382163b61369f5760405162461bcd60e51b815260206004820152601760248201527f41646170746572206e6f7420696e697469616c697a65640000000000000000006044820152606401610a84565b915091565b60006136b561010060ff8416614c1d565b61ffff166001901b8317905092915050565b825160005b8181101561394e5760008582815181106136e8576136e86149fc565b60200260200101519050600060086000613702848b612f80565b81526020810191909152604001600020546001600160a01b03169050806137635760405162461bcd60e51b8152602060048201526015602482015274149bdd5d19481b9bdd081dda1a5d195b1a5cdd1959605a1b6044820152606401610a84565b6000868481518110613777576137776149fc565b602002602001015113156139215760008a6001600160a01b031683838987815181106137a5576137a56149fc565b60209081029190910101516040516001600160a01b03938416602482015291831660448301526064820152908c16608482015260a40160408051601f198184030181529181526020820180516001600160e01b03166314b231d760e21b179052516138109190614882565b600060405180830381855af49150503d806000811461384b576040519150601f19603f3d011682016040523d82523d6000602084013e613850565b606091505b50509050806138715760405162461bcd60e51b8152600401610a849061489e565b868481518110613883576138836149fc565b602002602001015160076000856001600160a01b03166001600160a01b0316815260200190815260200160002060010160008282546138c29190614b03565b925050819055508684815181106138db576138db6149fc565b602002602001015160076000856001600160a01b03166001600160a01b03168152602001908152602001600020600201600082825461391a919061469f565b9091555050505b61394482868581518110613937576139376149fc565b6020026020010151613bf7565b50506001016136cc565b5050505050505050565b6040516370a0823160e01b81523060048201526000906001600160a01b038316906370a0823190602401602060405180830381865afa15801561399f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906139c391906146b6565b600c549091506000906001600160a01b0384811691161480156139f15750600654600160a01b900460ff1615155b6139fb5781613a08565b601054613a08908361469f565b6001600160a01b038416600090815260076020526040902060020154909150811115611100576001600160a01b038316600090815260076020526040902060020154613a54908261469f565b6001600160a01b03841660009081526007602052604081206001018054909190613a7f9084906147ea565b90915550506001600160a01b0383166000908152600760205260409020600201819055505050565b60038101548154600091613ac791600160a81b900463ffffffff16613cca565b905080826003016000828254613add919061469f565b90915550613aeb90506116de565b825463ffffffff91909116600160a81b0263ffffffff60a81b199091161790915550565b6000613b64826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316613d279092919063ffffffff16565b8051909150156111005780806020019051810190613b8291906145f7565b6111005760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610a84565b600082613bee8584613d36565b14949350505050565b6000670de0b6b3a7640000600f5483613c109190614656565b613c1a919061468b565b90506000613c28828461469f565b90508015613c91576001600160a01b03841660009081526007602052604081206003018054839290613c5b908490614829565b90915550506001600160a01b03841660009081526007602052604081206001018054839290613c8b908490614b03565b90915550505b8115611e85576001600160a01b0384166000908152600a602052604081208054849290613cbf908490614829565b909155505050505050565b60008082613cd66116de565b613ce0919061469f565b90506000670de0b6b3a764000082600e5487613cfc9190614656565b613d069190614656565b613d10919061468b565b9050848110613d1f5784612dfb565b949350505050565b60606135ce8484600085613daa565b600081815b8451811015613da2576000858281518110613d5857613d586149fc565b60200260200101519050808311613d7e5760008381526020829052604090209250613d8f565b600081815260208490526040902092505b5080613d9a81614aea565b915050613d3b565b509392505050565b606082471015613e0b5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401610a84565b6001600160a01b0385163b613e625760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610a84565b600080866001600160a01b03168587604051613e7e9190614882565b60006040518083038185875af1925050503d8060008114613ebb576040519150601f19603f3d011682016040523d82523d6000602084013e613ec0565b606091505b5091509150612cfe82828660608315613eda5750816135d1565b825115613eea5782518084602001fd5b8160405162461bcd60e51b8152600401610a849190614ad7565b6001600160a01b0381168114610dac57600080fd5b60008060408385031215613f2c57600080fd5b8235613f3781613f04565b946020939093013593505050565b600060208284031215613f5757600080fd5b5035919050565b600060208284031215613f7057600080fd5b81356135d181613f04565b8015158114610dac57600080fd5b600080600060608486031215613f9e57600080fd5b8335613fa981613f04565b9250602084013591506040840135613fc081613f7b565b809150509250925092565b600080600060608486031215613fe057600080fd5b833592506020840135613ff281613f04565b91506040840135613fc081613f04565b60006020828403121561401457600080fd5b81356135d181613f7b565b6000806040838503121561403257600080fd5b82359150602083013561404481613f04565b809150509250929050565b6000806000806080858703121561406557600080fd5b8435935060208501359250604085013561407e81613f04565b9150606085013561408e81613f7b565b939692955090935050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff811182821017156140d8576140d8614099565b604052919050565b600067ffffffffffffffff8211156140fa576140fa614099565b5060051b60200190565b600082601f83011261411557600080fd5b8135602061412a614125836140e0565b6140af565b82815260059290921b8401810191818101908684111561414957600080fd5b8286015b84811015614164578035835291830191830161414d565b509695505050505050565b803560ff8116811461255757600080fd5b600082601f83011261419157600080fd5b813560206141a1614125836140e0565b82815260059290921b840181019181810190868411156141c057600080fd5b8286015b848110156141645780356141d781613f04565b83529183019183016141c4565b60008083601f8401126141f657600080fd5b50813567ffffffffffffffff81111561420e57600080fd5b6020830191508360208260051b850101111561422957600080fd5b9250929050565b60008060008060008060008060006101008a8c03121561424f57600080fd5b8935985060208a0135975060408a013567ffffffffffffffff8082111561427557600080fd5b6142818d838e01614104565b985060608c013591508082111561429757600080fd5b6142a38d838e01614104565b975060808c01359150808211156142b957600080fd5b6142c58d838e01614104565b96506142d360a08d0161416f565b955060c08c01359150808211156142e957600080fd5b6142f58d838e01614180565b945060e08c013591508082111561430b57600080fd5b506143188c828d016141e4565b915080935050809150509295985092959850929598565b60008060008060008060a0878903121561434857600080fd5b863567ffffffffffffffff81111561435f57600080fd5b61436b89828a016141e4565b909750955061437e90506020880161416f565b93506040870135925060608701359150608087013590509295509295509295565b600080602083850312156143b257600080fd5b823567ffffffffffffffff8111156143c957600080fd5b6143d5858286016141e4565b90969095509350505050565b60005b838110156143fc5781810151838201526020016143e4565b83811115611e855750506000910152565b600081518084526144258160208601602086016143e1565b601f01601f19169290920160200192915050565b6000602080830181845280855180835260408601915060408160051b870101925083870160005b8281101561448e57603f1988860301845261447c85835161440d565b94509285019290850190600101614460565b5092979650505050505050565b6000602082840312156144ad57600080fd5b813563ffffffff811681146135d157600080fd5b600067ffffffffffffffff8211156144db576144db614099565b50601f01601f191660200190565b600080604083850312156144fc57600080fd5b82359150602083013567ffffffffffffffff81111561451a57600080fd5b8301601f8101851361452b57600080fd5b8035614539614125826144c1565b81815286602083850101111561454e57600080fd5b816020840160208301376000602083830101528093505050509250929050565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b6020808252601d908201527f50726f706f73616c2068617320756e636c61696d6564206c6561766573000000604082015260600190565b6000602082840312156145ec57600080fd5b81516135d181613f04565b60006020828403121561460957600080fd5b81516135d181613f7b565b60208082526012908201527110dbdb9d1c9858dd081a5cc81c185d5cd95960721b604082015260600190565b634e487b7160e01b600052601160045260246000fd5b600081600019048311821515161561467057614670614640565b500290565b634e487b7160e01b600052601260045260246000fd5b60008261469a5761469a614675565b500490565b6000828210156146b1576146b1614640565b500390565b6000602082840312156146c857600080fd5b5051919050565b600063ffffffff8083168185168083038211156146ee576146ee614640565b01949350505050565b85815263ffffffff851660208201526102006040820181905260009082015283516001600160a01b03166060820152610220810160208501516001600160a01b03811660808401525060408501516001600160a01b03811660a084015250606085015180151560c084015250608085015160e083015260a0850151610100818185015260c08701519150610120828186015260e0880151925061014083818701528289015161016087015281890151610180870152808901516101a0870152505050506147d06101c08301856001600160a01b03169052565b6001600160a01b0383166101e08301529695505050505050565b60008083128015600160ff1b85018412161561480857614808614640565b6001600160ff1b038401831381161561482357614823614640565b50500390565b6000821982111561483c5761483c614640565b500190565b600060ff82168061485457614854614640565b6000190192915050565b6001600160a01b03831681526040602082018190526000906135ce9083018461440d565b600082516148948184602087016143e1565b9190910192915050565b60208082526013908201527219195b1959d85d1958d85b1b0819985a5b1959606a1b604082015260600190565b600081518084526020808501945080840160005b838110156149045781516001600160a01b0316875295820195908201906001016148df565b509495945050505050565b600081518084526020808501945080840160005b8381101561490457815187529582019590820190600101614923565b85815260a06020820152600061495860a08301876148cb565b828103604084015261496a818761490f565b9050828103606084015261497e818661490f565b90508281036080840152614992818561490f565b98975050505050505050565b63ffffffff8616815260ff85166020820152608060408201819052810183905260006001600160fb1b038411156149d457600080fd5b8360051b808660a0850137600090830160a00190815260609092019290925295945050505050565b634e487b7160e01b600052603260045260246000fd5b6000808335601e19843603018112614a2957600080fd5b83018035915067ffffffffffffffff821115614a4457600080fd5b60200191503681900382131561422957600080fd5b8183823760009101908152919050565b600060208284031215614a7b57600080fd5b815167ffffffffffffffff811115614a9257600080fd5b8201601f81018413614aa357600080fd5b8051614ab1614125826144c1565b818152856020838501011115614ac657600080fd5b612dfb8260208301602086016143e1565b6020815260006135d1602083018461440d565b600060018201614afc57614afc614640565b5060010190565b600080821280156001600160ff1b0384900385131615614b2557614b25614640565b600160ff1b8390038412811615614b3e57614b3e614640565b50500190565b600060208284031215614b5657600080fd5b6040516020810181811067ffffffffffffffff82111715614b7957614b79614099565b6040529151825250919050565b60208152815160208201526000602083015160e06040840152614bad61010084018261490f565b90506040840151601f1980858403016060860152614bcb838361490f565b92506060860151915080858403016080860152614be8838361490f565b9250608086015160a086015260ff60a08701511660c086015260c08601519150808584030160e086015250612dfb82826148cb565b600061ffff80841680614c3257614c32614675565b9216919091069291505056fea2646970667358221220549293ed4d887e16dd2a172492d4a858240db4e1a4aebf6371749bb84a50094b64736f6c634300080d00330000000000000000000000007db69eb9f52ed773e9b03f5068a1ea0275b2fd9d00000000000000000000000040f941e48a552bf496b154af6bf55725f18d77c3000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000000000000000000
Deployed Bytecode
0x60806040526004361061028a5760003560e01c80635c975abb1161015a578063a5841194116100c1578063dd70e5e81161007a578063dd70e5e81461087f578063e0f339e31461089f578063e40064d7146108bf578063e460e35c146108ec578063f0056a7d1461090c578063f2fde38b146109b157610299565b8063a5841194146107ab578063ac9650d8146107cb578063b60c2d7d146107eb578063b9a3c84c1461080b578063c28f43921461083f578063cd9499951461085f57610299565b80637998a1c4116101135780637998a1c41461070157806380c09a821461071757806380f323a7146107375780638bda0c001461074d5780638da5cb5b1461076d578063a16fd6e91461078b57610299565b80635c975abb14610640578063625997c01461066a57806369b62502146102975780636ad0690a1461067f578063715018a6146106b857806376ec08dd146106cd57610299565b806322f8e566116101fe57806333dc09ca116101b757806333dc09ca146105125780633fc8cef3146105325780634144fd61146105665780634f7473ff146105f7578063566887001461060d57806356864f381461062057610299565b806322f8e56614610458578063240f475f1461047857806326205d801461049d57806329cb924d146104bd5780632d0f6f84146104d25780632d32d557146104f257610299565b80630ee28a88116102505780630ee28a881461039557806310b99527146103b557806311cfc159146103d557806316c38b3c146103eb5780631c39c38d1461040b57806322395aaa1461044357610299565b8062660b53146102a1578062c99206146102c1578063084d0513146102e157806309474ae2146103145780630c501af91461037557610299565b36610299576102976109d1565b005b6102976109d1565b3480156102ad57600080fd5b506102976102bc366004613f19565b610a5a565b3480156102cd57600080fd5b506102976102dc366004613f45565b610bae565b3480156102ed57600080fd5b506103016102fc366004613f19565b610daf565b6040519081526020015b60405180910390f35b34801561032057600080fd5b5061035561032f366004613f45565b600960205260009081526040902080546001909101546001600160a01b03918216911682565b604080516001600160a01b0393841681529290911660208301520161030b565b34801561038157600080fd5b50610297610390366004613f5e565b610ddb565b3480156103a157600080fd5b506102976103b0366004613f89565b610e82565b3480156103c157600080fd5b506102976103d0366004613fcb565b611105565b3480156103e157600080fd5b50610301600e5481565b3480156103f757600080fd5b50610297610406366004614002565b6111b3565b34801561041757600080fd5b5060005461042b906001600160a01b031681565b6040516001600160a01b03909116815260200161030b565b34801561044f57600080fd5b5061029761122f565b34801561046457600080fd5b50610297610473366004613f45565b611610565b34801561048457600080fd5b50600b5461042b9061010090046001600160a01b031681565b3480156104a957600080fd5b506102976104b8366004613f19565b61166b565b3480156104c957600080fd5b506103016116de565b3480156104de57600080fd5b506102976104ed366004613f5e565b61176f565b3480156104fe57600080fd5b5061042b61050d36600461401f565b6117f2565b34801561051e57600080fd5b5061029761052d366004613f19565b611823565b34801561053e57600080fd5b5061042b7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc281565b34801561057257600080fd5b506002546003546004546005546006546105b094939291906001600160a01b03811690600160a01b810460ff1690600160a81b900463ffffffff1687565b6040805197885260208801969096529486019390935260608501919091526001600160a01b0316608084015260ff1660a083015263ffffffff1660c082015260e00161030b565b34801561060357600080fd5b50610301600f5481565b61029761061b366004613f19565b611aa4565b34801561062c57600080fd5b5061029761063b36600461404f565b611da3565b34801561064c57600080fd5b50600b5461065a9060ff1681565b604051901515815260200161030b565b34801561067657600080fd5b50610297611e8b565b34801561068b57600080fd5b50600c546106a390600160a01b900463ffffffff1681565b60405163ffffffff909116815260200161030b565b3480156106c457600080fd5b50610297611fcb565b3480156106d957600080fd5b5061042b7f0000000000000000000000007db69eb9f52ed773e9b03f5068a1ea0275b2fd9d81565b34801561070d57600080fd5b50610301600d5481565b34801561072357600080fd5b50610297610732366004614230565b611fff565b34801561074357600080fd5b5061030160105481565b34801561075957600080fd5b5061029761076836600461432f565b612385565b34801561077957600080fd5b506001546001600160a01b031661042b565b34801561079757600080fd5b506103016107a6366004613f5e565b612530565b3480156107b757600080fd5b506102976107c6366004613f5e565b61255c565b6107de6107d936600461439f565b61257d565b60405161030b9190614439565b3480156107f757600080fd5b50610297610806366004613f5e565b612723565b34801561081757600080fd5b5061042b7f00000000000000000000000040f941e48a552bf496b154af6bf55725f18d77c381565b34801561084b57600080fd5b50600c5461042b906001600160a01b031681565b34801561086b57600080fd5b5061029761087a36600461449b565b6128e2565b34801561088b57600080fd5b5061029761089a3660046144e9565b6129bc565b3480156108ab57600080fd5b506103016108ba366004613f5e565b612a08565b3480156108cb57600080fd5b506103016108da366004613f5e565b600a6020526000908152604090205481565b3480156108f857600080fd5b50610297610907366004613fcb565b612a23565b34801561091857600080fd5b5061096f610927366004613f5e565b60076020526000908152604090208054600182015460028301546003909301546001600160a01b03831693600160a01b840460ff1693600160a81b900463ffffffff16929186565b604080516001600160a01b039097168752941515602087015263ffffffff909316938501939093526060840152608083019190915260a082015260c00161030b565b3480156109bd57600080fd5b506102976109cc366004613f5e565b612afc565b600054600160a01b900460ff1615610a58577f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031663d0e30db0346040518263ffffffff1660e01b81526004016000604051808303818588803b158015610a3e57600080fd5b505af1158015610a52573d6000803e3d6000fd5b50505050505b565b6001546001600160a01b03163314610a8d5760405162461bcd60e51b8152600401610a849061456e565b60405180910390fd5b610a95612b94565b610a9d612bed565b670de0b6b3a7640000811115610af55760405162461bcd60e51b815260206004820152601960248201527f4261642070726f746f636f6c46656543617074757265506374000000000000006044820152606401610a84565b6001600160a01b038216610b4b5760405162461bcd60e51b815260206004820152601d60248201527f4261642070726f746f636f6c46656543617074757265416464726573730000006044820152606401610a84565b600b8054610100600160a81b0319166101006001600160a01b03851690810291909117909155600f8290556040518291907fc1993b89fd79a19ece7beb067ddc8534ca26d29c0ff94ea2f53b4a508d1eedc990600090a3610baa612bfc565b5050565b6001546001600160a01b03163314610bd85760405162461bcd60e51b8152600401610a849061456e565b600654600160a01b900460ff1615610c025760405162461bcd60e51b8152600401610a84906145a3565b610c0a612b94565b610c12612bed565b6040516302abf57960e61b8152721259195b9d1a599a595c95da1a5d195b1a5cdd606a1b60048201526000907f00000000000000000000000040f941e48a552bf496b154af6bf55725f18d77c36001600160a01b03169063aafd5e4090602401602060405180830381865afa158015610c8f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610cb391906145da565b6040516390978d1b60e01b8152600481018490529091506001600160a01b038216906390978d1b90602401602060405180830381865afa158015610cfb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d1f91906145f7565b610d6b5760405162461bcd60e51b815260206004820152601860248201527f4964656e746966696572206e6f7420737570706f7274656400000000000000006044820152606401610a84565b600d8290556040518281527ff45367c278fcceff23d601ce4bdd191e5bd61687ff9f29dc7276a08fe54c0c5d9060200160405180910390a150610dac612bfc565b50565b6000610db9612b94565b610dc1612bed565b610dcb8383612c11565b9050610dd5612bfc565b92915050565b6001546001600160a01b03163314610e055760405162461bcd60e51b8152600401610a849061456e565b610e0d612b94565b610e15612bed565b6001600160a01b03818116600081815260076020908152604091829020805460ff60a01b1981169091558251938452909316928201929092527fac111b3b527b307393c94d98f26140effb71411054466818be97912d2d65f77691015b60405180910390a1610dac612bfc565b610e8a612b94565b610e92612bed565b600b5460ff1615610eb55760405162461bcd60e51b8152600401610a8490614614565b826001600160a01b03167f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b03161480610ef3575080155b610f2f5760405162461bcd60e51b815260206004820152600d60248201526c086c2dce840e6cadcc840cae8d609b1b6044820152606401610a84565b6000670de0b6b3a7640000610f4385612d09565b610f4d9085614656565b610f57919061468b565b6001600160a01b038581166000908152600760205260409081902054905163079cc67960e41b81523360048201526024810187905292935016906379cc6790906044016020604051808303816000875af1158015610fb9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fdd91906145f7565b506001600160a01b0384166000908152600760205260408120600201805483929061100990849061469f565b9091555050811561109d57604051632e1a7d4d60e01b8152600481018290527f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031690632e1a7d4d90602401600060405180830381600087803b15801561107657600080fd5b505af115801561108a573d6000803e3d6000fd5b505050506110983382612e04565b6110b1565b6110b16001600160a01b0385163383612f1d565b604080518281526020810185905233916001600160a01b038716917fcda1185f28599e6bd14ab8a68b3c30a11e1dce4256b5e67e94dd3fd846a6c589910160405180910390a350611100612bfc565b505050565b6001546001600160a01b0316331461112f5760405162461bcd60e51b8152600401610a849061456e565b611137612b94565b61113f612bed565b806008600061114e8587612f80565b815260208101919091526040908101600090812080546001600160a01b0319166001600160a01b0394851617905590518383169285169186917f234e7af08f77827792cc909447f27d2e6a3e2d839b04e26b50b71704a131c8a89190a4611100612bfc565b6001546001600160a01b031633146111dd5760405162461bcd60e51b8152600401610a849061456e565b6111e5612b94565b6111ed612bed565b600b805460ff19168215159081179091556040517f0e2fb031ee032dc02d8011dc50b816eb450cf856abd8261680dac74f72165bd290600090a2610dac612bfc565b611237612b94565b61123f612bed565b60006112496116de565b60065490915063ffffffff600160a81b909104811690821611156112af5760405162461bcd60e51b815260206004820152601760248201527f5265717565737420706173736564206c6976656e6573730000000000000000006044820152606401610a84565b60006112b9612fbd565b905060105481106112d3576112cc6130c3565b50506115e9565b60006112dd6131a2565b601054600c549192506112fb916001600160a01b0316908390613246565b806001600160a01b031663af355d1e600d5485600c60009054906101000a90046001600160a01b0316600087601054611334919061469f565b600c5460065460405160e089901b6001600160e01b0319168152600481019790975263ffffffff9586166024880152610120604488015260006101248801526001600160a01b039485166064880152608487019390935260a4860191909152600160a01b900490921660c48401521660e4820152670de0b6b3a7640000610104820152610144016020604051808303816000875af19250505080156113f6575060408051601f3d908101601f191682019092526113f3918101906146b6565b60015b61140a576114026130c3565b5050506115e9565b600c54611422906001600160a01b03168360006132f8565b5060408051610160810182526006546001600160a01b039081168252600060208301819052600c549182169383019390935260608201839052670de0b6b3a7640000608083015260a0820183905260c082019061148c90600160a01b900463ffffffff16876146cf565b63ffffffff16815260200160008152602001848152602001846010546114b2919061469f565b8152600c5463ffffffff600160a01b820481166020909301929092526000600281905560038190556004819055600555600680546001600160c81b0319169055601054929350611512926001600160a01b03909116913391309161340d16565b601054600c5461152f916001600160a01b03909116908490613246565b600d5460405163139c641960e31b81526001600160a01b03841691639ce320c891611565919088908690339030906004016146f7565b6020604051808303816000875af1158015611584573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115a891906146b6565b5060405163ffffffff8516815233907f15951cb2ef6993bc23a55912e7d0bcac13e4797c432aaa334816aed6914a7a909060200160405180910390a2505050505b6116086115f46131a2565b600c546001600160a01b03169060006132f8565b610a58612bfc565b6000546001600160a01b031661162557600080fd5b60005460405163117c72b360e11b8152600481018390526001600160a01b03909116906322f8e56690602401600060405180830381600087803b158015610a3e57600080fd5b6001546001600160a01b031633146116955760405162461bcd60e51b8152600401610a849061456e565b61169d612b94565b6116a5612bed565b6001600160a01b038216600090815260076020526040812060010180548392906116d09084906147ea565b90915550610baa9050612bfc565b600080546001600160a01b03161561176a5760008054906101000a90046001600160a01b03166001600160a01b03166329cb924d6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611741573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061176591906146b6565b905090565b504290565b611777612b94565b61177f612bed565b6001600160a01b038082166000818152600a602052604081208054919055600b5490926117b3929161010090041683612f1d565b60405181906001600160a01b038416907f74740239d7d696c84422b720e125e1f47c4138c66d1f4d2a48e99f4197cdb79c90600090a350610dac612bfc565b6000600860006118028486612f80565b81526020810191909152604001600020546001600160a01b03169392505050565b6001546001600160a01b0316331461184d5760405162461bcd60e51b8152600401610a849061456e565b600654600160a01b900460ff16156118775760405162461bcd60e51b8152600401610a84906145a3565b61187f612b94565b611887612bed565b806000036118d75760405162461bcd60e51b815260206004820152601760248201527f626f6e6420657175616c20746f2066696e616c206665650000000000000000006044820152606401610a84565b6040516302abf57960e61b81527210dbdb1b185d195c985b15da1a5d195b1a5cdd606a1b60048201526000907f00000000000000000000000040f941e48a552bf496b154af6bf55725f18d77c36001600160a01b03169063aafd5e4090602401602060405180830381865afa158015611954573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061197891906145da565b604051631d1d5b3960e11b81526001600160a01b03858116600483015291925090821690633a3ab67290602401602060405180830381865afa1580156119c2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119e691906145f7565b611a255760405162461bcd60e51b815260206004820152601060248201526f139bdd081bdb881dda1a5d195b1a5cdd60821b6044820152606401610a84565b600c80546001600160a01b0319166001600160a01b0385161790556000611a4a612fbd565b611a549084614829565b60108190556040518181529091506001600160a01b038516907fbfa9a96010167e98ce8c004f718932cbbfd33a58d681c752e693be7d457a1b3b9060200160405180910390a25050610baa612bfc565b611aac612b94565b611ab4612bed565b600b5460ff1615611ad75760405162461bcd60e51b8152600401610a8490614614565b6001600160a01b038216600090815260076020526040902054600160a01b900460ff16611b3a5760405162461bcd60e51b8152602060048201526011602482015270151bdad95b881b9bdd08195b98589b1959607a1b6044820152606401610a84565b816001600160a01b03167f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b0316148015611b7a57508034145b80611b83575034155b611bbf5760405162461bcd60e51b815260206004820152600d60248201526c426164206d73672e76616c756560981b6044820152606401610a84565b6000611bca83612d09565b611bdc83670de0b6b3a7640000614656565b611be6919061468b565b6001600160a01b038416600090815260076020526040812060020180549293508492909190611c16908490614829565b90915550506001600160a01b03838116600090815260076020526040908190205490516340c10f1960e01b8152336004820152602481018490529116906340c10f19906044016020604051808303816000875af1158015611c7b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c9f91906145f7565b50826001600160a01b03167f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b0316148015611ce15750600034115b15611d3f57826001600160a01b031663d0e30db0346040518263ffffffff1660e01b81526004016000604051808303818588803b158015611d2157600080fd5b505af1158015611d35573d6000803e3d6000fd5b5050505050611d54565b611d546001600160a01b03841633308561340d565b604080518381526020810183905233916001600160a01b038616917f3c69701a61c79a92ef9692903aaa0068bce8771361ecb09547391e4fb4df8537910160405180910390a350610baa612bfc565b611dab612b94565b611db3612bed565b6001546001600160a01b03163314611ddd5760405162461bcd60e51b8152600401610a849061456e565b6040516001600160a01b0383166024820152604481018490528115156064820152611e3690859060840160408051601f198184030181529190526020810180516001600160e01b031663272751c760e01b179052613445565b816001600160a01b031683857fb7d00a563842efb2c121a0eb02b7bb7ba1a34625bbc3d65057f1f0dbec0ec2a184604051611e75911515815260200190565b60405180910390a4611e85612bfc565b50505050565b6001546001600160a01b03163314611eb55760405162461bcd60e51b8152600401610a849061456e565b611ebd612b94565b611ec5612bed565b6040805160e0810182526002805482526003805460208401526004805494840194909452600580546060850152600680546001600160a01b038116608087015260ff600160a01b82041660a0870181905263ffffffff600160a81b83041660c088015260009586905593859055958490559290556001600160c81b031990931690559015611f6d576080810151601054600c54611f6d926001600160a01b0390911691612f1d565b80608001516001600160a01b0316816020015182600001517f993cba33f9b140c9ce20ba10d7eda92128d5beb6df856f064916108a11647a738460400151604051611fba91815260200190565b60405180910390a450610a58612bfc565b6001546001600160a01b03163314611ff55760405162461bcd60e51b8152600401610a849061456e565b610a586000613549565b612007612b94565b61200f612bed565b600b5460ff16156120325760405162461bcd60e51b8152600401610a8490614614565b600654600160a81b900463ffffffff1661204a6116de565b1161208d5760405162461bcd60e51b81526020600482015260136024820152724e6f7420706173736564206c6976656e65737360681b6044820152606401610a84565b600554600160ff86161b908116036120d95760405162461bcd60e51b815260206004820152600f60248201526e105b1c9958591e4818db185a5b5959608a1b6044820152606401610a84565b6121516002600001546040518060e001604052808c81526020018a81526020018981526020018881526020018b81526020018760ff1681526020018681525084848080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525061359b92505050565b6121895760405162461bcd60e51b81526020600482015260096024820152682130b210283937b7b360b91b6044820152606401610a84565b6000806121958b6135d8565b915091506121a8600260030154876136a4565b600555600680546014906121c590600160a01b900460ff16614841565b91906101000a81548160ff021916908360ff1602179055506121eb82828d888c8e6136c7565b896000036122ed57600354600454604051602481019290925260448201526000906001600160a01b03841690839060640160408051601f198184030181529181526020820180516001600160e01b031663124e93e160e21b1790525161225592919060240161485e565b60408051601f198184030181529181526020820180516001600160e01b0316637375c56f60e11b1790525161228a9190614882565b600060405180830381855af49150503d80600081146122c5576040519150601f19603f3d011682016040523d82523d6000602084013e6122ca565b606091505b50509050806122eb5760405162461bcd60e51b8152600401610a849061489e565b505b600654600160a01b900460ff1660000361232257600654601054600c54612322926001600160a01b0391821692911690612f1d565b336001600160a01b03168b8760ff167ff652dd63b1aedbf9e740f3152fb67b0d94d069cf1182811ebd88921850d935678d898e8e8e60405161236895949392919061493f565b60405180910390a4505061237a612bfc565b505050505050505050565b61238d612b94565b612395612bed565b600654600160a01b900460ff16156123bf5760405162461bcd60e51b8152600401610a84906145a3565b600b5460ff16156123e25760405162461bcd60e51b8152600401610a8490614614565b60008460ff16116124355760405162461bcd60e51b815260206004820181905260248201527f42756e646c65206d7573742068617665206174206c656173742031206c6561666044820152606401610a84565b600c54600090600160a01b900463ffffffff166124506116de565b61245a91906146cf565b60006005556006805460028790556003869055600485905560ff8816600160a01b0263ffffffff808516600160a81b0260ff60a01b19166001600160c81b031990931692909217176001600160a01b03191633908117909255601054600c549394506124d4936001600160a01b0316929130919061340d16565b336001600160a01b031683857f3185fa6fac8e91dc65e7424a8081c73353151d2715bddb71db0982c1fe4c0fd484898c8c8960405161251795949392919061499e565b60405180910390a450612528612bfc565b505050505050565b600061253a612b94565b612542612bed565b61254d826000612c11565b9050612557612bfc565b919050565b612564612b94565b61256c612bed565b61257581613958565b610dac612bfc565b606034156125cd5760405162461bcd60e51b815260206004820152601b60248201527f4f6e6c79206d756c746963616c6c207769746820302076616c756500000000006044820152606401610a84565b8167ffffffffffffffff8111156125e6576125e6614099565b60405190808252806020026020018201604052801561261957816020015b60608152602001906001900390816126045790505b50905060005b8281101561271c576000803086868581811061263d5761263d6149fc565b905060200281019061264f9190614a12565b60405161265d929190614a59565b600060405180830381855af49150503d8060008114612698576040519150601f19603f3d011682016040523d82523d6000602084013e61269d565b606091505b5091509150816126e9576044815110156126b657600080fd5b600481019050808060200190518101906126d09190614a69565b60405162461bcd60e51b8152600401610a849190614ad7565b808484815181106126fc576126fc6149fc565b60200260200101819052505050808061271490614aea565b91505061261f565b5092915050565b6001546001600160a01b0316331461274d5760405162461bcd60e51b8152600401610a849061456e565b612755612b94565b61275d612bed565b6001600160a01b038181166000908152600760205260409020541661287b57604051637e178db760e11b81526001600160a01b0382811660048301527f0000000000000000000000007db69eb9f52ed773e9b03f5068a1ea0275b2fd9d169063fc2f1b6e906024016020604051808303816000875af11580156127e4573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061280891906145da565b6001600160a01b03828116600090815260076020526040902080546001600160a01b0319169290911691909117905561283f6116de565b6001600160a01b0382166000908152600760205260409020805463ffffffff92909216600160a81b0263ffffffff60a81b199092169190911790555b6001600160a01b038181166000818152600760209081526040918290208054600160a01b60ff60a01b198216179091558251938452909316928201929092527f04e291c80180d65a57b5bf1bed775777ec0d6f283ef34bcf130712714d8bb7f79101610e72565b6001546001600160a01b0316331461290c5760405162461bcd60e51b8152600401610a849061456e565b612914612b94565b61291c612bed565b6102588163ffffffff16116129685760405162461bcd60e51b8152602060048201526012602482015271131a5d995b995cdcc81d1bdbc81cda1bdc9d60721b6044820152606401610a84565b600c805463ffffffff60a01b1916600160a01b63ffffffff8416908102919091179091556040519081527f04dd1d84d387f404568a7954b5e398518bdd716e1a8f4a790be9a1a225ad934790602001610e72565b6001546001600160a01b031633146129e65760405162461bcd60e51b8152600401610a849061456e565b6129ee612b94565b6129f6612bed565b612a008282613445565b610baa612bfc565b6000612a12612b94565b612a1a612bed565b61254d82612d09565b6001546001600160a01b03163314612a4d5760405162461bcd60e51b8152600401610a849061456e565b612a55612b94565b612a5d612bed565b6040805180820182526001600160a01b03848116808352848216602080850182815260008a815260098352879020955186549086166001600160a01b031991821617875590516001909601805496909516951694909417909255835187815292830152918101919091527f36050d958750e6ac3aa674ac7bbe8d0ae6a2f7d4b808e8c2c42c1f22fc9fc4bb9060600160405180910390a1611100612bfc565b6001546001600160a01b03163314612b265760405162461bcd60e51b8152600401610a849061456e565b6001600160a01b038116612b8b5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401610a84565b610dac81613549565b600054600160a01b900460ff16610a585760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610a84565b6000805460ff60a01b19169055565b6000805460ff60a01b1916600160a01b179055565b6000612c1c83613958565b6001600160a01b038381166000908152600760209081526040808320815160c08101835281549586168152600160a01b860460ff16151593810193909352600160a81b90940463ffffffff16908201526001830154606082018190526002840154608083015260039093015460a0820152918112612c9b576000612ca1565b81606001515b90506000612caf8286614829565b90506000828460800151612cc39190614829565b905080600003612ce157670de0b6b3a7640000945050505050610dd5565b80612cf483670de0b6b3a7640000614656565b612cfe919061468b565b979650505050505050565b6001600160a01b038082166000908152600760209081526040808320805482516318160ddd60e01b8152925194959194869491909216926318160ddd92600480830193928290030181865afa158015612d66573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d8a91906146b6565b905080600003612da55750670de0b6b3a76400009392505050565b612dae82613aa7565b612db784613958565b6000826003015483600101548460020154612dd29190614b03565b612ddc91906147ea565b905081612df182670de0b6b3a7640000614656565b612dfb919061468b565b95945050505050565b80471015612e545760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a20696e73756666696369656e742062616c616e63650000006044820152606401610a84565b6000826001600160a01b03168260405160006040518083038185875af1925050503d8060008114612ea1576040519150601f19603f3d011682016040523d82523d6000602084013e612ea6565b606091505b50509050806111005760405162461bcd60e51b815260206004820152603a60248201527f416464726573733a20756e61626c6520746f2073656e642076616c75652c207260448201527f6563697069656e74206d617920686176652072657665727465640000000000006064820152608401610a84565b6040516001600160a01b03831660248201526044810182905261110090849063a9059cbb60e01b906064015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152613b0f565b604080516001600160a01b038416602082015290810182905260009060600160405160208183030381529060405280519060200120905092915050565b6040516302abf57960e61b81526453746f726560d81b60048201526000907f00000000000000000000000040f941e48a552bf496b154af6bf55725f18d77c36001600160a01b03169063aafd5e4090602401602060405180830381865afa15801561302c573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061305091906145da565b600c54604051635b97aadd60e01b81526001600160a01b039182166004820152911690635b97aadd90602401602060405180830381865afa158015613099573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130bd9190614b44565b51919050565b600c5460065460105460405163a9059cbb60e01b81526001600160a01b039283166004820152602481019190915291169063a9059cbb906044016020604051808303816000875af115801561311c573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061314091906145f7565b506000600281905560038190556004819055600555600680546001600160c81b0319169055337f0cfbbf45ab7f5225663454de7117b1b0ed5a7c133b61f54ccf367dcf8b6d4d5961318f6116de565b60405190815260200160405180910390a2565b6040516302abf57960e61b815275536b696e6e794f7074696d69737469634f7261636c6560501b60048201526000907f00000000000000000000000040f941e48a552bf496b154af6bf55725f18d77c36001600160a01b03169063aafd5e4090602401602060405180830381865afa158015613222573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061176591906145da565b604051636eb1769f60e11b81523060048201526001600160a01b038381166024830152600091839186169063dd62ed3e90604401602060405180830381865afa158015613297573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132bb91906146b6565b6132c59190614829565b6040516001600160a01b038516602482015260448101829052909150611e8590859063095ea7b360e01b90606401612f49565b8015806133725750604051636eb1769f60e11b81523060048201526001600160a01b03838116602483015284169063dd62ed3e90604401602060405180830381865afa15801561334c573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061337091906146b6565b155b6133dd5760405162461bcd60e51b815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527520746f206e6f6e2d7a65726f20616c6c6f77616e636560501b6064820152608401610a84565b6040516001600160a01b03831660248201526044810182905261110090849063095ea7b360e01b90606401612f49565b6040516001600160a01b0380851660248301528316604482015260648101829052611e859085906323b872dd60e01b90608401612f49565b600080613451846135d8565b915091506000826001600160a01b0316828560405160240161347492919061485e565b60408051601f198184030181529181526020820180516001600160e01b0316637375c56f60e11b179052516134a99190614882565b600060405180830381855af49150503d80600081146134e4576040519150601f19603f3d011682016040523d82523d6000602084013e6134e9565b606091505b505090508061350a5760405162461bcd60e51b8152600401610a849061489e565b847f218987b934c2f6bc596136829fbf43a5fef4d6fafce41f3f6254d9a870c2deec8560405161353a9190614ad7565b60405180910390a25050505050565b600180546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b60006135ce8285856040516020016135b39190614b86565b60405160208183030381529060405280519060200120613be1565b90505b9392505050565b600081815260096020526040902080546001909101546001600160a01b039182169116806136485760405162461bcd60e51b815260206004820152601960248201527f53706f6b65506f6f6c206e6f7420696e697469616c697a6564000000000000006044820152606401610a84565b6001600160a01b0382163b61369f5760405162461bcd60e51b815260206004820152601760248201527f41646170746572206e6f7420696e697469616c697a65640000000000000000006044820152606401610a84565b915091565b60006136b561010060ff8416614c1d565b61ffff166001901b8317905092915050565b825160005b8181101561394e5760008582815181106136e8576136e86149fc565b60200260200101519050600060086000613702848b612f80565b81526020810191909152604001600020546001600160a01b03169050806137635760405162461bcd60e51b8152602060048201526015602482015274149bdd5d19481b9bdd081dda1a5d195b1a5cdd1959605a1b6044820152606401610a84565b6000868481518110613777576137776149fc565b602002602001015113156139215760008a6001600160a01b031683838987815181106137a5576137a56149fc565b60209081029190910101516040516001600160a01b03938416602482015291831660448301526064820152908c16608482015260a40160408051601f198184030181529181526020820180516001600160e01b03166314b231d760e21b179052516138109190614882565b600060405180830381855af49150503d806000811461384b576040519150601f19603f3d011682016040523d82523d6000602084013e613850565b606091505b50509050806138715760405162461bcd60e51b8152600401610a849061489e565b868481518110613883576138836149fc565b602002602001015160076000856001600160a01b03166001600160a01b0316815260200190815260200160002060010160008282546138c29190614b03565b925050819055508684815181106138db576138db6149fc565b602002602001015160076000856001600160a01b03166001600160a01b03168152602001908152602001600020600201600082825461391a919061469f565b9091555050505b61394482868581518110613937576139376149fc565b6020026020010151613bf7565b50506001016136cc565b5050505050505050565b6040516370a0823160e01b81523060048201526000906001600160a01b038316906370a0823190602401602060405180830381865afa15801561399f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906139c391906146b6565b600c549091506000906001600160a01b0384811691161480156139f15750600654600160a01b900460ff1615155b6139fb5781613a08565b601054613a08908361469f565b6001600160a01b038416600090815260076020526040902060020154909150811115611100576001600160a01b038316600090815260076020526040902060020154613a54908261469f565b6001600160a01b03841660009081526007602052604081206001018054909190613a7f9084906147ea565b90915550506001600160a01b0383166000908152600760205260409020600201819055505050565b60038101548154600091613ac791600160a81b900463ffffffff16613cca565b905080826003016000828254613add919061469f565b90915550613aeb90506116de565b825463ffffffff91909116600160a81b0263ffffffff60a81b199091161790915550565b6000613b64826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316613d279092919063ffffffff16565b8051909150156111005780806020019051810190613b8291906145f7565b6111005760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610a84565b600082613bee8584613d36565b14949350505050565b6000670de0b6b3a7640000600f5483613c109190614656565b613c1a919061468b565b90506000613c28828461469f565b90508015613c91576001600160a01b03841660009081526007602052604081206003018054839290613c5b908490614829565b90915550506001600160a01b03841660009081526007602052604081206001018054839290613c8b908490614b03565b90915550505b8115611e85576001600160a01b0384166000908152600a602052604081208054849290613cbf908490614829565b909155505050505050565b60008082613cd66116de565b613ce0919061469f565b90506000670de0b6b3a764000082600e5487613cfc9190614656565b613d069190614656565b613d10919061468b565b9050848110613d1f5784612dfb565b949350505050565b60606135ce8484600085613daa565b600081815b8451811015613da2576000858281518110613d5857613d586149fc565b60200260200101519050808311613d7e5760008381526020829052604090209250613d8f565b600081815260208490526040902092505b5080613d9a81614aea565b915050613d3b565b509392505050565b606082471015613e0b5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401610a84565b6001600160a01b0385163b613e625760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610a84565b600080866001600160a01b03168587604051613e7e9190614882565b60006040518083038185875af1925050503d8060008114613ebb576040519150601f19603f3d011682016040523d82523d6000602084013e613ec0565b606091505b5091509150612cfe82828660608315613eda5750816135d1565b825115613eea5782518084602001fd5b8160405162461bcd60e51b8152600401610a849190614ad7565b6001600160a01b0381168114610dac57600080fd5b60008060408385031215613f2c57600080fd5b8235613f3781613f04565b946020939093013593505050565b600060208284031215613f5757600080fd5b5035919050565b600060208284031215613f7057600080fd5b81356135d181613f04565b8015158114610dac57600080fd5b600080600060608486031215613f9e57600080fd5b8335613fa981613f04565b9250602084013591506040840135613fc081613f7b565b809150509250925092565b600080600060608486031215613fe057600080fd5b833592506020840135613ff281613f04565b91506040840135613fc081613f04565b60006020828403121561401457600080fd5b81356135d181613f7b565b6000806040838503121561403257600080fd5b82359150602083013561404481613f04565b809150509250929050565b6000806000806080858703121561406557600080fd5b8435935060208501359250604085013561407e81613f04565b9150606085013561408e81613f7b565b939692955090935050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff811182821017156140d8576140d8614099565b604052919050565b600067ffffffffffffffff8211156140fa576140fa614099565b5060051b60200190565b600082601f83011261411557600080fd5b8135602061412a614125836140e0565b6140af565b82815260059290921b8401810191818101908684111561414957600080fd5b8286015b84811015614164578035835291830191830161414d565b509695505050505050565b803560ff8116811461255757600080fd5b600082601f83011261419157600080fd5b813560206141a1614125836140e0565b82815260059290921b840181019181810190868411156141c057600080fd5b8286015b848110156141645780356141d781613f04565b83529183019183016141c4565b60008083601f8401126141f657600080fd5b50813567ffffffffffffffff81111561420e57600080fd5b6020830191508360208260051b850101111561422957600080fd5b9250929050565b60008060008060008060008060006101008a8c03121561424f57600080fd5b8935985060208a0135975060408a013567ffffffffffffffff8082111561427557600080fd5b6142818d838e01614104565b985060608c013591508082111561429757600080fd5b6142a38d838e01614104565b975060808c01359150808211156142b957600080fd5b6142c58d838e01614104565b96506142d360a08d0161416f565b955060c08c01359150808211156142e957600080fd5b6142f58d838e01614180565b945060e08c013591508082111561430b57600080fd5b506143188c828d016141e4565b915080935050809150509295985092959850929598565b60008060008060008060a0878903121561434857600080fd5b863567ffffffffffffffff81111561435f57600080fd5b61436b89828a016141e4565b909750955061437e90506020880161416f565b93506040870135925060608701359150608087013590509295509295509295565b600080602083850312156143b257600080fd5b823567ffffffffffffffff8111156143c957600080fd5b6143d5858286016141e4565b90969095509350505050565b60005b838110156143fc5781810151838201526020016143e4565b83811115611e855750506000910152565b600081518084526144258160208601602086016143e1565b601f01601f19169290920160200192915050565b6000602080830181845280855180835260408601915060408160051b870101925083870160005b8281101561448e57603f1988860301845261447c85835161440d565b94509285019290850190600101614460565b5092979650505050505050565b6000602082840312156144ad57600080fd5b813563ffffffff811681146135d157600080fd5b600067ffffffffffffffff8211156144db576144db614099565b50601f01601f191660200190565b600080604083850312156144fc57600080fd5b82359150602083013567ffffffffffffffff81111561451a57600080fd5b8301601f8101851361452b57600080fd5b8035614539614125826144c1565b81815286602083850101111561454e57600080fd5b816020840160208301376000602083830101528093505050509250929050565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b6020808252601d908201527f50726f706f73616c2068617320756e636c61696d6564206c6561766573000000604082015260600190565b6000602082840312156145ec57600080fd5b81516135d181613f04565b60006020828403121561460957600080fd5b81516135d181613f7b565b60208082526012908201527110dbdb9d1c9858dd081a5cc81c185d5cd95960721b604082015260600190565b634e487b7160e01b600052601160045260246000fd5b600081600019048311821515161561467057614670614640565b500290565b634e487b7160e01b600052601260045260246000fd5b60008261469a5761469a614675565b500490565b6000828210156146b1576146b1614640565b500390565b6000602082840312156146c857600080fd5b5051919050565b600063ffffffff8083168185168083038211156146ee576146ee614640565b01949350505050565b85815263ffffffff851660208201526102006040820181905260009082015283516001600160a01b03166060820152610220810160208501516001600160a01b03811660808401525060408501516001600160a01b03811660a084015250606085015180151560c084015250608085015160e083015260a0850151610100818185015260c08701519150610120828186015260e0880151925061014083818701528289015161016087015281890151610180870152808901516101a0870152505050506147d06101c08301856001600160a01b03169052565b6001600160a01b0383166101e08301529695505050505050565b60008083128015600160ff1b85018412161561480857614808614640565b6001600160ff1b038401831381161561482357614823614640565b50500390565b6000821982111561483c5761483c614640565b500190565b600060ff82168061485457614854614640565b6000190192915050565b6001600160a01b03831681526040602082018190526000906135ce9083018461440d565b600082516148948184602087016143e1565b9190910192915050565b60208082526013908201527219195b1959d85d1958d85b1b0819985a5b1959606a1b604082015260600190565b600081518084526020808501945080840160005b838110156149045781516001600160a01b0316875295820195908201906001016148df565b509495945050505050565b600081518084526020808501945080840160005b8381101561490457815187529582019590820190600101614923565b85815260a06020820152600061495860a08301876148cb565b828103604084015261496a818761490f565b9050828103606084015261497e818661490f565b90508281036080840152614992818561490f565b98975050505050505050565b63ffffffff8616815260ff85166020820152608060408201819052810183905260006001600160fb1b038411156149d457600080fd5b8360051b808660a0850137600090830160a00190815260609092019290925295945050505050565b634e487b7160e01b600052603260045260246000fd5b6000808335601e19843603018112614a2957600080fd5b83018035915067ffffffffffffffff821115614a4457600080fd5b60200191503681900382131561422957600080fd5b8183823760009101908152919050565b600060208284031215614a7b57600080fd5b815167ffffffffffffffff811115614a9257600080fd5b8201601f81018413614aa357600080fd5b8051614ab1614125826144c1565b818152856020838501011115614ac657600080fd5b612dfb8260208301602086016143e1565b6020815260006135d1602083018461440d565b600060018201614afc57614afc614640565b5060010190565b600080821280156001600160ff1b0384900385131615614b2557614b25614640565b600160ff1b8390038412811615614b3e57614b3e614640565b50500190565b600060208284031215614b5657600080fd5b6040516020810181811067ffffffffffffffff82111715614b7957614b79614099565b6040529151825250919050565b60208152815160208201526000602083015160e06040840152614bad61010084018261490f565b90506040840151601f1980858403016060860152614bcb838361490f565b92506060860151915080858403016080860152614be8838361490f565b9250608086015160a086015260ff60a08701511660c086015260c08601519150808584030160e086015250612dfb82826148cb565b600061ffff80841680614c3257614c32614675565b9216919091069291505056fea2646970667358221220549293ed4d887e16dd2a172492d4a858240db4e1a4aebf6371749bb84a50094b64736f6c634300080d0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000007db69eb9f52ed773e9b03f5068a1ea0275b2fd9d00000000000000000000000040f941e48a552bf496b154af6bf55725f18d77c3000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000000000000000000
-----Decoded View---------------
Arg [0] : _lpTokenFactory (address): 0x7dB69eb9F52eD773E9b03f5068A1ea0275b2fD9d
Arg [1] : _finder (address): 0x40f941E48A552bF496B154Af6bf55725f18D77c3
Arg [2] : _weth (address): 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2
Arg [3] : _timer (address): 0x0000000000000000000000000000000000000000
-----Encoded View---------------
4 Constructor Arguments found :
Arg [0] : 0000000000000000000000007db69eb9f52ed773e9b03f5068a1ea0275b2fd9d
Arg [1] : 00000000000000000000000040f941e48a552bf496b154af6bf55725f18d77c3
Arg [2] : 000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2
Arg [3] : 0000000000000000000000000000000000000000000000000000000000000000
Loading...
Loading
Loading...
Loading
Net Worth in USD
$32,139,635.83
Net Worth in ETH
16,266.405266
Token Allocations
WBTC
66.85%
WETH
13.04%
ACX
5.87%
Others
14.24%
Multichain Portfolio | 34 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|---|---|---|---|---|
| ETH | 66.85% | $76,149 | 282.1518 | $21,485,579.01 | |
| ETH | 13.04% | $1,975.83 | 2,121.7704 | $4,192,255.49 | |
| ETH | 5.87% | $0.035284 | 53,438,280.4538 | $1,885,501.86 | |
| ETH | 4.36% | $0.9998 | 1,403,089.9101 | $1,402,809.29 | |
| ETH | 4.28% | $0.999705 | 1,376,516.1494 | $1,376,110.08 | |
| ETH | 1.96% | $0.999916 | 631,116.1943 | $631,063.18 | |
| ETH | 1.55% | $0.384107 | 1,296,807.1928 | $498,112.72 | |
| ETH | 1.31% | $1 | 419,277.4345 | $420,954.54 | |
| ETH | 0.56% | $0.43076 | 415,183.6947 | $178,844.53 | |
| ETH | 0.10% | $0.462408 | 69,367.5108 | $32,076.09 | |
| ETH | 0.05% | $0.05244 | 284,436.5119 | $14,915.85 | |
| ETH | 0.02% | $0.14591 | 54,942.9475 | $8,016.73 | |
| ETH | 0.02% | $1,975.83 | 3.4177 | $6,752.81 | |
| ETH | 0.02% | $0.000752 | 7,425,691.704 | $5,581 | |
| ETH | <0.01% | $0.138363 | 156.4957 | $21.65 | |
| ETH | <0.01% | $0.022285 | 195.6007 | $4.36 | |
| ETH | <0.01% | $0.370028 | 1.751 | $0.6479 | |
| ETH | <0.01% | $0.000264 | 1,000 | $0.2639 | |
| ETH | <0.01% | $0.00 | 0.0154 | $0.00 | |
| ARB | <0.01% | $1,975.8 | 0.5212 | $1,029.74 | |
| BSC | <0.01% | $622.3 | 0.00657698 | $4.09 | |
| BASE | <0.01% | $1,976.75 | 0.000556 | $1.1 | |
| BASE | <0.01% | $0.00 | 100 | $0.00 | |
| OP | <0.01% | $1,975.67 | 0.00025 | $0.493917 | |
| POL | <0.01% | $0.107595 | 0.1007 | $0.010838 |
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.