Source Code
| Transaction Hash |
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
Latest 25 internal transactions (View All)
Advanced mode:
| Parent Transaction Hash | Block | From | To | |||
|---|---|---|---|---|---|---|
| 31222011 | 3 hrs ago | 0.83414501 FRAX | ||||
| 31222011 | 3 hrs ago | 0.27873052 FRAX | ||||
| 31221978 | 3 hrs ago | 3.1744344 FRAX | ||||
| 31221978 | 3 hrs ago | 3.1744344 FRAX | ||||
| 31213049 | 8 hrs ago | 0.03262101 FRAX | ||||
| 31213049 | 8 hrs ago | 0.03262101 FRAX | ||||
| 31212997 | 8 hrs ago | 2.59045529 FRAX | ||||
| 31212997 | 8 hrs ago | 2.59045529 FRAX | ||||
| 31142172 | 47 hrs ago | 9.04950246 FRAX | ||||
| 31142172 | 47 hrs ago | 9.04950246 FRAX | ||||
| 31112645 | 2 days ago | 1.16718731 FRAX | ||||
| 31112645 | 2 days ago | 0.27873052 FRAX | ||||
| 31112617 | 2 days ago | 1.29859066 FRAX | ||||
| 31112617 | 2 days ago | 1.29859066 FRAX | ||||
| 31112574 | 2 days ago | 0.23571587 FRAX | ||||
| 31112574 | 2 days ago | 0.23571587 FRAX | ||||
| 31084394 | 3 days ago | 0.94227159 FRAX | ||||
| 31084394 | 3 days ago | 0.27873052 FRAX | ||||
| 31011921 | 5 days ago | 0.93019234 FRAX | ||||
| 31011921 | 5 days ago | 0.27873052 FRAX | ||||
| 31010702 | 5 days ago | 0.73462345 FRAX | ||||
| 31010702 | 5 days ago | 0.27873052 FRAX | ||||
| 31010702 | 5 days ago | 0.01946353 FRAX | ||||
| 31010324 | 5 days ago | 0.94227159 FRAX | ||||
| 31010324 | 5 days ago | 0.27873052 FRAX |
Cross-Chain Transactions
Loading...
Loading
Contract Name:
UniversalRouter
Compiler Version
v0.8.29+commit.ab55807c
Optimization Enabled:
Yes with 20000 runs
Other Settings:
cancun EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.24;
// Command implementations
import {Dispatcher} from './base/Dispatcher.sol';
import {RouterDeployParameters} from './types/RouterDeployParameters.sol';
import {PaymentsImmutables, PaymentsParameters} from './modules/PaymentsImmutables.sol';
import {RouterImmutables, RouterParameters} from './modules/uniswap/RouterImmutables.sol';
import {V4SwapRouter} from './modules/uniswap/v4/V4SwapRouter.sol';
import {Commands} from './libraries/Commands.sol';
import {IUniversalRouter} from './interfaces/IUniversalRouter.sol';
contract UniversalRouter is IUniversalRouter, Dispatcher {
constructor(RouterDeployParameters memory params)
RouterImmutables(
RouterParameters(
params.v2Factory,
params.v3Factory,
params.pairInitCodeHash,
params.poolInitCodeHash,
params.veloV2Factory,
params.veloCLFactory,
params.veloV2InitCodeHash,
params.veloCLInitCodeHash
)
)
V4SwapRouter(params.v4PoolManager)
PaymentsImmutables(PaymentsParameters(params.permit2, params.weth9))
{}
modifier checkDeadline(uint256 deadline) {
if (block.timestamp > deadline) revert TransactionDeadlinePassed();
_;
}
/// @notice To receive ETH from WETH
receive() external payable {
if (msg.sender != address(WETH9) && msg.sender != address(poolManager)) revert InvalidEthSender();
}
/// @inheritdoc IUniversalRouter
function execute(bytes calldata commands, bytes[] calldata inputs, uint256 deadline)
external
payable
checkDeadline(deadline)
{
execute(commands, inputs);
}
/// @inheritdoc Dispatcher
function execute(bytes calldata commands, bytes[] calldata inputs) public payable override isNotLocked {
bool success;
bytes memory output;
uint256 numCommands = commands.length;
if (inputs.length != numCommands) revert LengthMismatch();
// loop through all given commands, execute them and pass along outputs as defined
for (uint256 commandIndex = 0; commandIndex < numCommands; commandIndex++) {
bytes1 command = commands[commandIndex];
bytes calldata input = inputs[commandIndex];
(success, output) = dispatch(command, input);
if (!success && successRequired(command)) {
revert ExecutionFailed({commandIndex: commandIndex, message: output});
}
}
}
function successRequired(bytes1 command) internal pure returns (bool) {
return command & Commands.FLAG_ALLOW_REVERT == 0;
}
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.24;
import {ERC20} from 'solmate/src/tokens/ERC20.sol';
import {TypeCasts} from '@hyperlane/core/contracts/libs/TypeCasts.sol';
import {IPostDispatchHook} from '@hyperlane/core/contracts/interfaces/hooks/IPostDispatchHook.sol';
import {IAllowanceTransfer} from 'permit2/src/interfaces/IAllowanceTransfer.sol';
import {ActionConstants} from '@uniswap/v4-periphery/src/libraries/ActionConstants.sol';
import {CalldataDecoder} from '@uniswap/v4-periphery/src/libraries/CalldataDecoder.sol';
import {PoolKey} from '@uniswap/v4-core/src/types/PoolKey.sol';
import {IPoolManager} from '@uniswap/v4-core/src/interfaces/IPoolManager.sol';
import {IInterchainAccountRouter} from '../interfaces/external/IInterchainAccountRouter.sol';
import {V2SwapRouter} from '../modules/uniswap/v2/V2SwapRouter.sol';
import {V3SwapRouter} from '../modules/uniswap/v3/V3SwapRouter.sol';
import {V4SwapRouter} from '../modules/uniswap/v4/V4SwapRouter.sol';
import {BytesLib} from '../modules/uniswap/v3/BytesLib.sol';
import {Payments} from '../modules/Payments.sol';
import {BridgeRouter} from '../modules/bridge/BridgeRouter.sol';
import {Commands} from '../libraries/Commands.sol';
import {Constants} from '../libraries/Constants.sol';
import {Lock} from './Lock.sol';
/// @title Decodes and Executes Commands
/// @notice Called by the UniversalRouter contract to efficiently decode and execute a singular command
abstract contract Dispatcher is Payments, V2SwapRouter, V3SwapRouter, V4SwapRouter, BridgeRouter, Lock {
using BytesLib for bytes;
using CalldataDecoder for bytes;
error InvalidCommandType(uint256 commandType);
error BalanceTooLow();
event UniversalRouterSwap(address indexed sender, address indexed recipient);
event UniversalRouterBridge(
address indexed sender, address indexed recipient, address indexed token, uint256 amount, uint32 domain
);
event CrossChainSwap(
address indexed caller, address indexed localRouter, uint32 indexed destinationDomain, bytes32 commitment
);
/// @notice Executes encoded commands along with provided inputs.
/// @param commands A set of concatenated commands, each 1 byte in length
/// @param inputs An array of byte strings containing abi encoded inputs for each command
function execute(bytes calldata commands, bytes[] calldata inputs) external payable virtual;
/// @notice Public view function to be used instead of msg.sender, as the contract performs self-reentrancy and at
/// times msg.sender == address(this). Instead msgSender() returns the initiator of the lock
/// @dev overrides BaseActionsRouter.msgSender in V4Router
function msgSender() public view override returns (address) {
return _getLocker();
}
/// @notice Decodes and executes the given command with the given inputs
/// @param commandType The command type to execute
/// @param inputs The inputs to execute the command with
/// @dev 2 masks are used to enable use of a nested-if statement in execution for efficiency reasons
/// @return success True on success of the command, false on failure
/// @return output The outputs or error messages, if any, from the command
function dispatch(bytes1 commandType, bytes calldata inputs) internal returns (bool success, bytes memory output) {
uint256 command = uint8(commandType & Commands.COMMAND_TYPE_MASK);
success = true;
// 0x00 <= command < 0x21
if (command < Commands.EXECUTE_SUB_PLAN) {
// 0x00 <= command < 0x10
if (command < Commands.V4_SWAP) {
// 0x00 <= command < 0x08
if (command < Commands.V2_SWAP_EXACT_IN) {
if (command == Commands.V3_SWAP_EXACT_IN) {
// equivalent: abi.decode(inputs, (address, uint256, uint256, bytes, bool, bool))
address recipient;
uint256 amountIn;
uint256 amountOutMin;
bool payerIsUser;
bool isUni;
assembly {
recipient := calldataload(inputs.offset)
amountIn := calldataload(add(inputs.offset, 0x20))
amountOutMin := calldataload(add(inputs.offset, 0x40))
// 0x60 offset is the path, decoded below
payerIsUser := calldataload(add(inputs.offset, 0x80))
isUni := calldataload(add(inputs.offset, 0xA0))
}
bytes calldata path = inputs.toBytes(3);
address payer = payerIsUser ? msgSender() : address(this);
recipient = map(recipient);
v3SwapExactInput({
recipient: recipient,
amountIn: amountIn,
amountOutMinimum: amountOutMin,
path: path,
payer: payer,
isUni: isUni
});
emit UniversalRouterSwap({sender: msgSender(), recipient: recipient});
} else if (command == Commands.V3_SWAP_EXACT_OUT) {
// equivalent: abi.decode(inputs, (address, uint256, uint256, bytes, bool, bool))
address recipient;
uint256 amountOut;
uint256 amountInMax;
bool payerIsUser;
bool isUni;
assembly {
recipient := calldataload(inputs.offset)
amountOut := calldataload(add(inputs.offset, 0x20))
amountInMax := calldataload(add(inputs.offset, 0x40))
// 0x60 offset is the path, decoded below
payerIsUser := calldataload(add(inputs.offset, 0x80))
isUni := calldataload(add(inputs.offset, 0xA0))
}
bytes calldata path = inputs.toBytes(3);
address payer = payerIsUser ? msgSender() : address(this);
recipient = map(recipient);
v3SwapExactOutput({
recipient: recipient,
amountOut: amountOut,
amountInMaximum: amountInMax,
path: path,
payer: payer,
isUni: isUni
});
emit UniversalRouterSwap({sender: msgSender(), recipient: recipient});
} else if (command == Commands.PERMIT2_TRANSFER_FROM) {
// equivalent: abi.decode(inputs, (address, address, uint160))
address token;
address recipient;
uint160 amount;
assembly {
token := calldataload(inputs.offset)
recipient := calldataload(add(inputs.offset, 0x20))
amount := calldataload(add(inputs.offset, 0x40))
}
permit2TransferFrom(token, msgSender(), map(recipient), amount);
} else if (command == Commands.PERMIT2_PERMIT_BATCH) {
IAllowanceTransfer.PermitBatch calldata permitBatch;
assembly {
// this is a variable length struct, so calldataload(inputs.offset) contains the
// offset from inputs.offset at which the struct begins
permitBatch := add(inputs.offset, calldataload(inputs.offset))
}
bytes calldata data = inputs.toBytes(1);
(success, output) = address(PERMIT2).call(
abi.encodeWithSignature(
'permit(address,((address,uint160,uint48,uint48)[],address,uint256),bytes)',
msgSender(),
permitBatch,
data
)
);
} else if (command == Commands.SWEEP) {
// equivalent: abi.decode(inputs, (address, address, uint256))
address token;
address recipient;
uint160 amountMin;
assembly {
token := calldataload(inputs.offset)
recipient := calldataload(add(inputs.offset, 0x20))
amountMin := calldataload(add(inputs.offset, 0x40))
}
Payments.sweep(token, map(recipient), amountMin);
} else if (command == Commands.TRANSFER) {
// equivalent: abi.decode(inputs, (address, address, uint256))
address token;
address recipient;
uint256 value;
assembly {
token := calldataload(inputs.offset)
recipient := calldataload(add(inputs.offset, 0x20))
value := calldataload(add(inputs.offset, 0x40))
}
Payments.pay(token, map(recipient), value);
} else if (command == Commands.PAY_PORTION) {
// equivalent: abi.decode(inputs, (address, address, uint256))
address token;
address recipient;
uint256 bips;
assembly {
token := calldataload(inputs.offset)
recipient := calldataload(add(inputs.offset, 0x20))
bips := calldataload(add(inputs.offset, 0x40))
}
Payments.payPortion(token, map(recipient), bips);
} else if (command == Commands.TRANSFER_FROM) {
// equivalent: abi.decode(inputs, (address, address, uint256))
address token;
address recipient;
uint256 value;
assembly {
token := calldataload(inputs.offset)
recipient := calldataload(add(inputs.offset, 0x20))
value := calldataload(add(inputs.offset, 0x40))
}
address payer = msgSender();
if (value == Constants.TOTAL_BALANCE) value = ERC20(token).balanceOf(payer);
payOrPermit2Transfer({token: token, payer: payer, recipient: map(recipient), amount: value});
}
} else {
// 0x08 <= command < 0x10
if (command == Commands.V2_SWAP_EXACT_IN) {
// equivalent: abi.decode(inputs, (address, uint256, uint256, bytes, bool, bool))
address recipient;
uint256 amountIn;
uint256 amountOutMin;
bool payerIsUser;
bool isUni;
assembly {
recipient := calldataload(inputs.offset)
amountIn := calldataload(add(inputs.offset, 0x20))
amountOutMin := calldataload(add(inputs.offset, 0x40))
// 0x60 offset is the path, decoded below
payerIsUser := calldataload(add(inputs.offset, 0x80))
isUni := calldataload(add(inputs.offset, 0xA0))
}
bytes calldata path = inputs.toBytes(3);
address payer = payerIsUser ? msgSender() : address(this);
recipient = map(recipient);
v2SwapExactInput({
recipient: recipient,
amountIn: amountIn,
amountOutMinimum: amountOutMin,
path: path,
payer: payer,
isUni: isUni
});
emit UniversalRouterSwap({sender: msgSender(), recipient: recipient});
} else if (command == Commands.V2_SWAP_EXACT_OUT) {
// equivalent: abi.decode(inputs, (address, uint256, uint256, bytes, bool, bool))
address recipient;
uint256 amountOut;
uint256 amountInMax;
bool payerIsUser;
bool isUni;
assembly {
recipient := calldataload(inputs.offset)
amountOut := calldataload(add(inputs.offset, 0x20))
amountInMax := calldataload(add(inputs.offset, 0x40))
// 0x60 offset is the path, decoded below
payerIsUser := calldataload(add(inputs.offset, 0x80))
isUni := calldataload(add(inputs.offset, 0xA0))
}
bytes calldata path = inputs.toBytes(3);
address payer = payerIsUser ? msgSender() : address(this);
recipient = map(recipient);
v2SwapExactOutput({
recipient: recipient,
amountOut: amountOut,
amountInMaximum: amountInMax,
path: path,
payer: payer,
isUni: isUni
});
emit UniversalRouterSwap({sender: msgSender(), recipient: recipient});
} else if (command == Commands.PERMIT2_PERMIT) {
// equivalent: abi.decode(inputs, (IAllowanceTransfer.PermitSingle, bytes))
IAllowanceTransfer.PermitSingle calldata permitSingle;
assembly {
permitSingle := inputs.offset
}
bytes calldata data = inputs.toBytes(6); // PermitSingle takes first 6 slots (0..5)
(success, output) = address(PERMIT2).call(
abi.encodeWithSignature(
'permit(address,((address,uint160,uint48,uint48),address,uint256),bytes)',
msgSender(),
permitSingle,
data
)
);
} else if (command == Commands.WRAP_ETH) {
// equivalent: abi.decode(inputs, (address, uint256))
address recipient;
uint256 amount;
assembly {
recipient := calldataload(inputs.offset)
amount := calldataload(add(inputs.offset, 0x20))
}
Payments.wrapETH(map(recipient), amount);
} else if (command == Commands.UNWRAP_WETH) {
// equivalent: abi.decode(inputs, (address, uint256))
address recipient;
uint256 amountMin;
assembly {
recipient := calldataload(inputs.offset)
amountMin := calldataload(add(inputs.offset, 0x20))
}
Payments.unwrapWETH9(map(recipient), amountMin);
} else if (command == Commands.PERMIT2_TRANSFER_FROM_BATCH) {
IAllowanceTransfer.AllowanceTransferDetails[] calldata batchDetails;
(uint256 length, uint256 offset) = inputs.toLengthOffset(0);
assembly {
batchDetails.length := length
batchDetails.offset := offset
}
permit2TransferFrom(batchDetails, msgSender());
} else if (command == Commands.BALANCE_CHECK_ERC20) {
// equivalent: abi.decode(inputs, (address, address, uint256))
address owner;
address token;
uint256 minBalance;
assembly {
owner := calldataload(inputs.offset)
token := calldataload(add(inputs.offset, 0x20))
minBalance := calldataload(add(inputs.offset, 0x40))
}
success = (ERC20(token).balanceOf(owner) >= minBalance);
if (!success) output = abi.encodePacked(BalanceTooLow.selector);
} else {
// placeholder area for command 0x0f
revert InvalidCommandType(command);
}
}
} else {
// 0x10 <= command < 0x21
if (command == Commands.V4_SWAP) {
// pass the calldata provided to V4SwapRouter._executeActions (defined in BaseActionsRouter)
_executeActions(inputs);
// This contract MUST be approved to spend the token since its going to be doing the call on the position manager
} else if (command == Commands.V4_INITIALIZE_POOL) {
PoolKey calldata poolKey;
uint160 sqrtPriceX96;
assembly {
poolKey := inputs.offset
sqrtPriceX96 := calldataload(add(inputs.offset, 0xa0))
}
(success, output) =
address(poolManager).call(abi.encodeCall(IPoolManager.initialize, (poolKey, sqrtPriceX96)));
} else if (command == Commands.BRIDGE_TOKEN) {
// equivalent: abi.decode(inputs, (uint8, address, address, address, uint256, uint256, uint32, bool))
uint8 bridgeType;
address recipient;
address token;
address bridge;
uint256 amount;
uint256 msgFee;
uint32 domain;
bool payerIsUser;
assembly {
bridgeType := calldataload(inputs.offset)
recipient := calldataload(add(inputs.offset, 0x20))
token := calldataload(add(inputs.offset, 0x40))
bridge := calldataload(add(inputs.offset, 0x60))
amount := calldataload(add(inputs.offset, 0x80))
msgFee := calldataload(add(inputs.offset, 0xA0))
domain := calldataload(add(inputs.offset, 0xC0))
payerIsUser := calldataload(add(inputs.offset, 0xE0))
}
address sender = msgSender();
address payer = payerIsUser ? sender : address(this);
recipient = recipient == ActionConstants.MSG_SENDER ? sender : recipient;
if (amount == ActionConstants.CONTRACT_BALANCE) amount = ERC20(token).balanceOf(address(this));
bridgeToken({
bridgeType: bridgeType,
sender: sender,
recipient: recipient,
token: token,
bridge: bridge,
amount: amount,
msgFee: msgFee,
domain: domain,
payer: payer
});
emit UniversalRouterBridge({
sender: sender,
recipient: recipient,
token: token,
amount: amount,
domain: domain
});
} else if (command == Commands.EXECUTE_CROSS_CHAIN) {
// equivalent: abi.decode(inputs, (uint32, address, bytes32, bytes32, bytes32, uint256, address, bytes))
uint32 domain;
address icaRouter;
bytes32 remoteRouter;
bytes32 ism;
bytes32 commitment;
uint256 msgFee;
address hook;
assembly {
domain := calldataload(inputs.offset)
icaRouter := calldataload(add(inputs.offset, 0x20))
remoteRouter := calldataload(add(inputs.offset, 0x40))
ism := calldataload(add(inputs.offset, 0x60))
commitment := calldataload(add(inputs.offset, 0x80))
msgFee := calldataload(add(inputs.offset, 0xA0))
hook := calldataload(add(inputs.offset, 0xC0))
// 0xE0 offset contains the hook metadata, decoded below
}
bytes calldata hookMetadata = inputs.toBytes(7);
IInterchainAccountRouter(icaRouter).callRemoteCommitReveal{value: msgFee}({
_destination: domain,
_router: remoteRouter,
_ism: ism,
_hookMetadata: hookMetadata,
_hook: IPostDispatchHook(hook),
_salt: TypeCasts.addressToBytes32(msgSender()),
_commitment: commitment
});
emit CrossChainSwap({
caller: msgSender(),
localRouter: icaRouter,
destinationDomain: domain,
commitment: commitment
});
} else {
// placeholder area for commands 0x14-0x20
revert InvalidCommandType(command);
}
}
} else {
// 0x21 <= command
if (command == Commands.EXECUTE_SUB_PLAN) {
(bytes calldata _commands, bytes[] calldata _inputs) = inputs.decodeCommandsAndInputs();
(success, output) = (address(this)).call(abi.encodeCall(Dispatcher.execute, (_commands, _inputs)));
} else {
// placeholder area for commands 0x22-0x3f
revert InvalidCommandType(command);
}
}
}
/// @notice Calculates the recipient address for a command
/// @param recipient The recipient or recipient-flag for the command
/// @return output The resultant recipient for the command
function map(address recipient) internal view returns (address) {
if (recipient == ActionConstants.MSG_SENDER) {
return msgSender();
} else if (recipient == ActionConstants.ADDRESS_THIS) {
return address(this);
} else {
return recipient;
}
}
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.24;
struct RouterDeployParameters {
// Payment parameters
address permit2;
address weth9;
// Uniswap swapping parameters
address v2Factory;
address v3Factory;
bytes32 pairInitCodeHash;
bytes32 poolInitCodeHash;
address v4PoolManager;
// Velodrome swapping parameters
address veloV2Factory;
address veloCLFactory;
bytes32 veloV2InitCodeHash;
bytes32 veloCLInitCodeHash;
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.24;
import {IWETH9} from '@uniswap/v4-periphery/src/interfaces/external/IWETH9.sol';
import {IPermit2} from 'permit2/src/interfaces/IPermit2.sol';
import {IPaymentImmutables} from '../interfaces/IPaymentImmutables.sol';
struct PaymentsParameters {
address permit2;
address weth9;
}
contract PaymentsImmutables is IPaymentImmutables {
/// @inheritdoc IPaymentImmutables
IWETH9 public immutable WETH9;
/// @inheritdoc IPaymentImmutables
IPermit2 public immutable PERMIT2;
constructor(PaymentsParameters memory params) {
WETH9 = IWETH9(params.weth9);
PERMIT2 = IPermit2(params.permit2);
}
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.24;
import {IRouterImmutables} from '../../interfaces/IRouterImmutables.sol';
struct RouterParameters {
address v2Factory;
address v3Factory;
bytes32 pairInitCodeHash;
bytes32 poolInitCodeHash;
address veloV2Factory;
address veloCLFactory;
bytes32 veloV2InitCodeHash;
bytes32 veloCLInitCodeHash;
}
contract RouterImmutables is IRouterImmutables {
///@inheritdoc IRouterImmutables
address public immutable UNISWAP_V2_FACTORY;
///@inheritdoc IRouterImmutables
bytes32 public immutable UNISWAP_V2_PAIR_INIT_CODE_HASH;
///@inheritdoc IRouterImmutables
address public immutable UNISWAP_V3_FACTORY;
///@inheritdoc IRouterImmutables
bytes32 public immutable UNISWAP_V3_POOL_INIT_CODE_HASH;
///@inheritdoc IRouterImmutables
address public immutable VELODROME_V2_FACTORY;
///@inheritdoc IRouterImmutables
bytes32 public immutable VELODROME_V2_INIT_CODE_HASH;
///@inheritdoc IRouterImmutables
address public immutable VELODROME_CL_FACTORY;
///@inheritdoc IRouterImmutables
bytes32 public immutable VELODROME_CL_POOL_INIT_CODE_HASH;
constructor(RouterParameters memory params) {
UNISWAP_V2_FACTORY = params.v2Factory;
UNISWAP_V2_PAIR_INIT_CODE_HASH = params.pairInitCodeHash;
UNISWAP_V3_FACTORY = params.v3Factory;
UNISWAP_V3_POOL_INIT_CODE_HASH = params.poolInitCodeHash;
VELODROME_V2_FACTORY = params.veloV2Factory;
VELODROME_V2_INIT_CODE_HASH = params.veloV2InitCodeHash;
VELODROME_CL_FACTORY = params.veloCLFactory;
VELODROME_CL_POOL_INIT_CODE_HASH = params.veloCLInitCodeHash;
}
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.24;
import {RouterImmutables} from '../RouterImmutables.sol';
import {Permit2Payments} from '../../Permit2Payments.sol';
import {V4Router} from '@uniswap/v4-periphery/src/V4Router.sol';
import {IPoolManager} from '@uniswap/v4-core/src/interfaces/IPoolManager.sol';
import {Currency} from '@uniswap/v4-core/src/types/Currency.sol';
/// @title Router for Uniswap v4 Trades
abstract contract V4SwapRouter is V4Router, Permit2Payments {
constructor(address _poolManager) V4Router(IPoolManager(_poolManager)) {}
function _pay(Currency token, address payer, uint256 amount) internal override {
payOrPermit2Transfer(Currency.unwrap(token), payer, address(poolManager), amount);
}
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.24;
/// @title Commands
/// @notice Command Flags used to decode commands
library Commands {
// Masks to extract certain bits of commands
bytes1 internal constant FLAG_ALLOW_REVERT = 0x80;
bytes1 internal constant COMMAND_TYPE_MASK = 0x3f;
// Command Types. Maximum supported command at this moment is 0x3f.
// The commands are executed in nested if blocks to minimise gas consumption
// Command Types where value<=0x07, executed in the first nested-if block
uint256 constant V3_SWAP_EXACT_IN = 0x00;
uint256 constant V3_SWAP_EXACT_OUT = 0x01;
uint256 constant PERMIT2_TRANSFER_FROM = 0x02;
uint256 constant PERMIT2_PERMIT_BATCH = 0x03;
uint256 constant SWEEP = 0x04;
uint256 constant TRANSFER = 0x05;
uint256 constant PAY_PORTION = 0x06;
uint256 constant TRANSFER_FROM = 0x07;
// Command Types where 0x08<=value<=0x0f, executed in the second nested-if block
uint256 constant V2_SWAP_EXACT_IN = 0x08;
uint256 constant V2_SWAP_EXACT_OUT = 0x09;
uint256 constant PERMIT2_PERMIT = 0x0a;
uint256 constant WRAP_ETH = 0x0b;
uint256 constant UNWRAP_WETH = 0x0c;
uint256 constant PERMIT2_TRANSFER_FROM_BATCH = 0x0d;
uint256 constant BALANCE_CHECK_ERC20 = 0x0e;
// COMMAND_PLACEHOLDER = 0x0f;
// Command Types where 0x10<=value<=0x20, executed in the third nested-if block
uint256 constant V4_SWAP = 0x10;
uint256 constant V4_INITIALIZE_POOL = 0x11;
uint256 constant BRIDGE_TOKEN = 0x12;
uint256 constant EXECUTE_CROSS_CHAIN = 0x13;
// COMMAND_PLACEHOLDER = 0x14 -> 0x20
// Command Types where 0x21<=value<=0x3f
uint256 constant EXECUTE_SUB_PLAN = 0x21;
// COMMAND_PLACEHOLDER for 0x22 to 0x3f
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.24;
interface IUniversalRouter {
/// @notice Thrown when a required command has failed
error ExecutionFailed(uint256 commandIndex, bytes message);
/// @notice Thrown when attempting to send ETH directly to the contract
error ETHNotAccepted();
/// @notice Thrown when executing commands with an expired deadline
error TransactionDeadlinePassed();
/// @notice Thrown when attempting to execute commands and an incorrect number of inputs are provided
error LengthMismatch();
// @notice Thrown when an address that isn't WETH tries to send ETH to the router without calldata
error InvalidEthSender();
/// @notice Executes encoded commands along with provided inputs. Reverts if deadline has expired.
/// @param commands A set of concatenated commands, each 1 byte in length
/// @param inputs An array of byte strings containing abi encoded inputs for each command
/// @param deadline The deadline by which the transaction must be executed
function execute(bytes calldata commands, bytes[] calldata inputs, uint256 deadline) external payable;
}// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)
/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)
/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.
abstract contract ERC20 {
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
event Transfer(address indexed from, address indexed to, uint256 amount);
event Approval(address indexed owner, address indexed spender, uint256 amount);
/*//////////////////////////////////////////////////////////////
METADATA STORAGE
//////////////////////////////////////////////////////////////*/
string public name;
string public symbol;
uint8 public immutable decimals;
/*//////////////////////////////////////////////////////////////
ERC20 STORAGE
//////////////////////////////////////////////////////////////*/
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;
/*//////////////////////////////////////////////////////////////
EIP-2612 STORAGE
//////////////////////////////////////////////////////////////*/
uint256 internal immutable INITIAL_CHAIN_ID;
bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;
mapping(address => uint256) public nonces;
/*//////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/
constructor(
string memory _name,
string memory _symbol,
uint8 _decimals
) {
name = _name;
symbol = _symbol;
decimals = _decimals;
INITIAL_CHAIN_ID = block.chainid;
INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
}
/*//////////////////////////////////////////////////////////////
ERC20 LOGIC
//////////////////////////////////////////////////////////////*/
function approve(address spender, uint256 amount) public virtual returns (bool) {
allowance[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
return true;
}
function transfer(address to, uint256 amount) public virtual returns (bool) {
balanceOf[msg.sender] -= amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(msg.sender, to, amount);
return true;
}
function transferFrom(
address from,
address to,
uint256 amount
) public virtual returns (bool) {
uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.
if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;
balanceOf[from] -= amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(from, to, amount);
return true;
}
/*//////////////////////////////////////////////////////////////
EIP-2612 LOGIC
//////////////////////////////////////////////////////////////*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) public virtual {
require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");
// Unchecked because the only math done is incrementing
// the owner's nonce which cannot realistically overflow.
unchecked {
address recoveredAddress = ecrecover(
keccak256(
abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR(),
keccak256(
abi.encode(
keccak256(
"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
),
owner,
spender,
value,
nonces[owner]++,
deadline
)
)
)
),
v,
r,
s
);
require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");
allowance[recoveredAddress][spender] = value;
}
emit Approval(owner, spender, value);
}
function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
}
function computeDomainSeparator() internal view virtual returns (bytes32) {
return
keccak256(
abi.encode(
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
keccak256(bytes(name)),
keccak256("1"),
block.chainid,
address(this)
)
);
}
/*//////////////////////////////////////////////////////////////
INTERNAL MINT/BURN LOGIC
//////////////////////////////////////////////////////////////*/
function _mint(address to, uint256 amount) internal virtual {
totalSupply += amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(address(0), to, amount);
}
function _burn(address from, uint256 amount) internal virtual {
balanceOf[from] -= amount;
// Cannot underflow because a user's balance
// will never be larger than the total supply.
unchecked {
totalSupply -= amount;
}
emit Transfer(from, address(0), amount);
}
}// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity >=0.6.11;
library TypeCasts {
// alignment preserving cast
function addressToBytes32(address _addr) internal pure returns (bytes32) {
return bytes32(uint256(uint160(_addr)));
}
// alignment preserving cast
function bytes32ToAddress(bytes32 _buf) internal pure returns (address) {
require(
uint256(_buf) <= uint256(type(uint160).max),
"TypeCasts: bytes32ToAddress overflow"
);
return address(uint160(uint256(_buf)));
}
}// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity >=0.8.0;
/*@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@ HYPERLANE @@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@*/
interface IPostDispatchHook {
enum Types {
UNUSED,
ROUTING,
AGGREGATION,
MERKLE_TREE,
INTERCHAIN_GAS_PAYMASTER,
FALLBACK_ROUTING,
ID_AUTH_ISM,
PAUSABLE,
PROTOCOL_FEE,
LAYER_ZERO_V1,
RATE_LIMITED,
ARB_L2_TO_L1,
OP_L2_TO_L1,
MAILBOX_DEFAULT_HOOK,
AMOUNT_ROUTING
}
/**
* @notice Returns an enum that represents the type of hook
*/
function hookType() external view returns (uint8);
/**
* @notice Returns whether the hook supports metadata
* @param metadata metadata
* @return Whether the hook supports metadata
*/
function supportsMetadata(
bytes calldata metadata
) external view returns (bool);
/**
* @notice Post action after a message is dispatched via the Mailbox
* @param metadata The metadata required for the hook
* @param message The message passed from the Mailbox.dispatch() call
*/
function postDispatch(
bytes calldata metadata,
bytes calldata message
) external payable;
/**
* @notice Compute the payment required by the postDispatch call
* @param metadata The metadata required for the hook
* @param message The message passed from the Mailbox.dispatch() call
* @return Quoted payment for the postDispatch call
*/
function quoteDispatch(
bytes calldata metadata,
bytes calldata message
) external view returns (uint256);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {IEIP712} from "./IEIP712.sol";
/// @title AllowanceTransfer
/// @notice Handles ERC20 token permissions through signature based allowance setting and ERC20 token transfers by checking allowed amounts
/// @dev Requires user's token approval on the Permit2 contract
interface IAllowanceTransfer is IEIP712 {
/// @notice Thrown when an allowance on a token has expired.
/// @param deadline The timestamp at which the allowed amount is no longer valid
error AllowanceExpired(uint256 deadline);
/// @notice Thrown when an allowance on a token has been depleted.
/// @param amount The maximum amount allowed
error InsufficientAllowance(uint256 amount);
/// @notice Thrown when too many nonces are invalidated.
error ExcessiveInvalidation();
/// @notice Emits an event when the owner successfully invalidates an ordered nonce.
event NonceInvalidation(
address indexed owner, address indexed token, address indexed spender, uint48 newNonce, uint48 oldNonce
);
/// @notice Emits an event when the owner successfully sets permissions on a token for the spender.
event Approval(
address indexed owner, address indexed token, address indexed spender, uint160 amount, uint48 expiration
);
/// @notice Emits an event when the owner successfully sets permissions using a permit signature on a token for the spender.
event Permit(
address indexed owner,
address indexed token,
address indexed spender,
uint160 amount,
uint48 expiration,
uint48 nonce
);
/// @notice Emits an event when the owner sets the allowance back to 0 with the lockdown function.
event Lockdown(address indexed owner, address token, address spender);
/// @notice The permit data for a token
struct PermitDetails {
// ERC20 token address
address token;
// the maximum amount allowed to spend
uint160 amount;
// timestamp at which a spender's token allowances become invalid
uint48 expiration;
// an incrementing value indexed per owner,token,and spender for each signature
uint48 nonce;
}
/// @notice The permit message signed for a single token allowance
struct PermitSingle {
// the permit data for a single token alownce
PermitDetails details;
// address permissioned on the allowed tokens
address spender;
// deadline on the permit signature
uint256 sigDeadline;
}
/// @notice The permit message signed for multiple token allowances
struct PermitBatch {
// the permit data for multiple token allowances
PermitDetails[] details;
// address permissioned on the allowed tokens
address spender;
// deadline on the permit signature
uint256 sigDeadline;
}
/// @notice The saved permissions
/// @dev This info is saved per owner, per token, per spender and all signed over in the permit message
/// @dev Setting amount to type(uint160).max sets an unlimited approval
struct PackedAllowance {
// amount allowed
uint160 amount;
// permission expiry
uint48 expiration;
// an incrementing value indexed per owner,token,and spender for each signature
uint48 nonce;
}
/// @notice A token spender pair.
struct TokenSpenderPair {
// the token the spender is approved
address token;
// the spender address
address spender;
}
/// @notice Details for a token transfer.
struct AllowanceTransferDetails {
// the owner of the token
address from;
// the recipient of the token
address to;
// the amount of the token
uint160 amount;
// the token to be transferred
address token;
}
/// @notice A mapping from owner address to token address to spender address to PackedAllowance struct, which contains details and conditions of the approval.
/// @notice The mapping is indexed in the above order see: allowance[ownerAddress][tokenAddress][spenderAddress]
/// @dev The packed slot holds the allowed amount, expiration at which the allowed amount is no longer valid, and current nonce thats updated on any signature based approvals.
function allowance(address user, address token, address spender)
external
view
returns (uint160 amount, uint48 expiration, uint48 nonce);
/// @notice Approves the spender to use up to amount of the specified token up until the expiration
/// @param token The token to approve
/// @param spender The spender address to approve
/// @param amount The approved amount of the token
/// @param expiration The timestamp at which the approval is no longer valid
/// @dev The packed allowance also holds a nonce, which will stay unchanged in approve
/// @dev Setting amount to type(uint160).max sets an unlimited approval
function approve(address token, address spender, uint160 amount, uint48 expiration) external;
/// @notice Permit a spender to a given amount of the owners token via the owner's EIP-712 signature
/// @dev May fail if the owner's nonce was invalidated in-flight by invalidateNonce
/// @param owner The owner of the tokens being approved
/// @param permitSingle Data signed over by the owner specifying the terms of approval
/// @param signature The owner's signature over the permit data
function permit(address owner, PermitSingle memory permitSingle, bytes calldata signature) external;
/// @notice Permit a spender to the signed amounts of the owners tokens via the owner's EIP-712 signature
/// @dev May fail if the owner's nonce was invalidated in-flight by invalidateNonce
/// @param owner The owner of the tokens being approved
/// @param permitBatch Data signed over by the owner specifying the terms of approval
/// @param signature The owner's signature over the permit data
function permit(address owner, PermitBatch memory permitBatch, bytes calldata signature) external;
/// @notice Transfer approved tokens from one address to another
/// @param from The address to transfer from
/// @param to The address of the recipient
/// @param amount The amount of the token to transfer
/// @param token The token address to transfer
/// @dev Requires the from address to have approved at least the desired amount
/// of tokens to msg.sender.
function transferFrom(address from, address to, uint160 amount, address token) external;
/// @notice Transfer approved tokens in a batch
/// @param transferDetails Array of owners, recipients, amounts, and tokens for the transfers
/// @dev Requires the from addresses to have approved at least the desired amount
/// of tokens to msg.sender.
function transferFrom(AllowanceTransferDetails[] calldata transferDetails) external;
/// @notice Enables performing a "lockdown" of the sender's Permit2 identity
/// by batch revoking approvals
/// @param approvals Array of approvals to revoke.
function lockdown(TokenSpenderPair[] calldata approvals) external;
/// @notice Invalidate nonces for a given (token, spender) pair
/// @param token The token to invalidate nonces for
/// @param spender The spender to invalidate nonces for
/// @param newNonce The new nonce to set. Invalidates all nonces less than it.
/// @dev Can't invalidate more than 2**16 nonces per transaction.
function invalidateNonces(address token, address spender, uint48 newNonce) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @title Action Constants
/// @notice Common constants used in actions
/// @dev Constants are gas efficient alternatives to their literal values
library ActionConstants {
/// @notice used to signal that an action should use the input value of the open delta on the pool manager
/// or of the balance that the contract holds
uint128 internal constant OPEN_DELTA = 0;
/// @notice used to signal that an action should use the contract's entire balance of a currency
/// This value is equivalent to 1<<255, i.e. a singular 1 in the most significant bit.
uint256 internal constant CONTRACT_BALANCE = 0x8000000000000000000000000000000000000000000000000000000000000000;
/// @notice used to signal that the recipient of an action should be the msgSender
address internal constant MSG_SENDER = address(1);
/// @notice used to signal that the recipient of an action should be the address(this)
address internal constant ADDRESS_THIS = address(2);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {Currency} from "@uniswap/v4-core/src/types/Currency.sol";
import {IV4Router} from "../interfaces/IV4Router.sol";
import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol";
/// @title Library for abi decoding in calldata
library CalldataDecoder {
using CalldataDecoder for bytes;
error SliceOutOfBounds();
/// @notice mask used for offsets and lengths to ensure no overflow
/// @dev no sane abi encoding will pass in an offset or length greater than type(uint32).max
/// (note that this does deviate from standard solidity behavior and offsets/lengths will
/// be interpreted as mod type(uint32).max which will only impact malicious/buggy callers)
uint256 constant OFFSET_OR_LENGTH_MASK = 0xffffffff;
uint256 constant OFFSET_OR_LENGTH_MASK_AND_WORD_ALIGN = 0xffffffe0;
/// @notice equivalent to SliceOutOfBounds.selector, stored in least-significant bits
uint256 constant SLICE_ERROR_SELECTOR = 0x3b99b53d;
/// @dev equivalent to: abi.decode(params, (bytes, bytes[])) in calldata (requires strict abi encoding)
function decodeActionsRouterParams(bytes calldata _bytes)
internal
pure
returns (bytes calldata actions, bytes[] calldata params)
{
assembly ("memory-safe") {
// Strict encoding requires that the data begin with:
// 0x00: 0x40 (offset to `actions.length`)
// 0x20: 0x60 + actions.length (offset to `params.length`)
// 0x40: `actions.length`
// 0x60: beginning of actions
// Verify actions offset matches strict encoding
let invalidData := xor(calldataload(_bytes.offset), 0x40)
actions.offset := add(_bytes.offset, 0x60)
actions.length := and(calldataload(add(_bytes.offset, 0x40)), OFFSET_OR_LENGTH_MASK)
// Round actions length up to be word-aligned, and add 0x60 (for the first 3 words of encoding)
let paramsLengthOffset := add(and(add(actions.length, 0x1f), OFFSET_OR_LENGTH_MASK_AND_WORD_ALIGN), 0x60)
// Verify params offset matches strict encoding
invalidData := or(invalidData, xor(calldataload(add(_bytes.offset, 0x20)), paramsLengthOffset))
let paramsLengthPointer := add(_bytes.offset, paramsLengthOffset)
params.length := and(calldataload(paramsLengthPointer), OFFSET_OR_LENGTH_MASK)
params.offset := add(paramsLengthPointer, 0x20)
// Expected offset for `params[0]` is params.length * 32
// As the first `params.length` slots are pointers to each of the array element lengths
let tailOffset := shl(5, params.length)
let expectedOffset := tailOffset
for { let offset := 0 } lt(offset, tailOffset) { offset := add(offset, 32) } {
let itemLengthOffset := calldataload(add(params.offset, offset))
// Verify that the offset matches the expected offset from strict encoding
invalidData := or(invalidData, xor(itemLengthOffset, expectedOffset))
let itemLengthPointer := add(params.offset, itemLengthOffset)
let length :=
add(and(add(calldataload(itemLengthPointer), 0x1f), OFFSET_OR_LENGTH_MASK_AND_WORD_ALIGN), 0x20)
expectedOffset := add(expectedOffset, length)
}
// if the data encoding was invalid, or the provided bytes string isnt as long as the encoding says, revert
if or(invalidData, lt(add(_bytes.length, _bytes.offset), add(params.offset, expectedOffset))) {
mstore(0, SLICE_ERROR_SELECTOR)
revert(0x1c, 4)
}
}
}
/// @dev equivalent to: abi.decode(params, (uint256, uint256, uint128, uint128, bytes)) in calldata
function decodeModifyLiquidityParams(bytes calldata params)
internal
pure
returns (uint256 tokenId, uint256 liquidity, uint128 amount0, uint128 amount1, bytes calldata hookData)
{
// no length check performed, as there is a length check in `toBytes`
assembly ("memory-safe") {
tokenId := calldataload(params.offset)
liquidity := calldataload(add(params.offset, 0x20))
amount0 := calldataload(add(params.offset, 0x40))
amount1 := calldataload(add(params.offset, 0x60))
}
hookData = params.toBytes(4);
}
/// @dev equivalent to: abi.decode(params, (uint256, uint128, uint128, bytes)) in calldata
function decodeIncreaseLiquidityFromDeltasParams(bytes calldata params)
internal
pure
returns (uint256 tokenId, uint128 amount0Max, uint128 amount1Max, bytes calldata hookData)
{
// no length check performed, as there is a length check in `toBytes`
assembly ("memory-safe") {
tokenId := calldataload(params.offset)
amount0Max := calldataload(add(params.offset, 0x20))
amount1Max := calldataload(add(params.offset, 0x40))
}
hookData = params.toBytes(3);
}
/// @dev equivalent to: abi.decode(params, (PoolKey, int24, int24, uint256, uint128, uint128, address, bytes)) in calldata
function decodeMintParams(bytes calldata params)
internal
pure
returns (
PoolKey calldata poolKey,
int24 tickLower,
int24 tickUpper,
uint256 liquidity,
uint128 amount0Max,
uint128 amount1Max,
address owner,
bytes calldata hookData
)
{
// no length check performed, as there is a length check in `toBytes`
assembly ("memory-safe") {
poolKey := params.offset
tickLower := calldataload(add(params.offset, 0xa0))
tickUpper := calldataload(add(params.offset, 0xc0))
liquidity := calldataload(add(params.offset, 0xe0))
amount0Max := calldataload(add(params.offset, 0x100))
amount1Max := calldataload(add(params.offset, 0x120))
owner := calldataload(add(params.offset, 0x140))
}
hookData = params.toBytes(11);
}
/// @dev equivalent to: abi.decode(params, (PoolKey, int24, int24, uint128, uint128, address, bytes)) in calldata
function decodeMintFromDeltasParams(bytes calldata params)
internal
pure
returns (
PoolKey calldata poolKey,
int24 tickLower,
int24 tickUpper,
uint128 amount0Max,
uint128 amount1Max,
address owner,
bytes calldata hookData
)
{
// no length check performed, as there is a length check in `toBytes`
assembly ("memory-safe") {
poolKey := params.offset
tickLower := calldataload(add(params.offset, 0xa0))
tickUpper := calldataload(add(params.offset, 0xc0))
amount0Max := calldataload(add(params.offset, 0xe0))
amount1Max := calldataload(add(params.offset, 0x100))
owner := calldataload(add(params.offset, 0x120))
}
hookData = params.toBytes(10);
}
/// @dev equivalent to: abi.decode(params, (uint256, uint128, uint128, bytes)) in calldata
function decodeBurnParams(bytes calldata params)
internal
pure
returns (uint256 tokenId, uint128 amount0Min, uint128 amount1Min, bytes calldata hookData)
{
// no length check performed, as there is a length check in `toBytes`
assembly ("memory-safe") {
tokenId := calldataload(params.offset)
amount0Min := calldataload(add(params.offset, 0x20))
amount1Min := calldataload(add(params.offset, 0x40))
}
hookData = params.toBytes(3);
}
/// @dev equivalent to: abi.decode(params, (IV4Router.ExactInputParams))
function decodeSwapExactInParams(bytes calldata params)
internal
pure
returns (IV4Router.ExactInputParams calldata swapParams)
{
// ExactInputParams is a variable length struct so we just have to look up its location
assembly ("memory-safe") {
// only safety checks for the minimum length, where path is empty
// 0xa0 = 5 * 0x20 -> 3 elements, path offset, and path length 0
if lt(params.length, 0xa0) {
mstore(0, SLICE_ERROR_SELECTOR)
revert(0x1c, 4)
}
swapParams := add(params.offset, calldataload(params.offset))
}
}
/// @dev equivalent to: abi.decode(params, (IV4Router.ExactInputSingleParams))
function decodeSwapExactInSingleParams(bytes calldata params)
internal
pure
returns (IV4Router.ExactInputSingleParams calldata swapParams)
{
// ExactInputSingleParams is a variable length struct so we just have to look up its location
assembly ("memory-safe") {
// only safety checks for the minimum length, where hookData is empty
// 0x140 = 10 * 0x20 -> 8 elements, bytes offset, and bytes length 0
if lt(params.length, 0x140) {
mstore(0, SLICE_ERROR_SELECTOR)
revert(0x1c, 4)
}
swapParams := add(params.offset, calldataload(params.offset))
}
}
/// @dev equivalent to: abi.decode(params, (IV4Router.ExactOutputParams))
function decodeSwapExactOutParams(bytes calldata params)
internal
pure
returns (IV4Router.ExactOutputParams calldata swapParams)
{
// ExactOutputParams is a variable length struct so we just have to look up its location
assembly ("memory-safe") {
// only safety checks for the minimum length, where path is empty
// 0xa0 = 5 * 0x20 -> 3 elements, path offset, and path length 0
if lt(params.length, 0xa0) {
mstore(0, SLICE_ERROR_SELECTOR)
revert(0x1c, 4)
}
swapParams := add(params.offset, calldataload(params.offset))
}
}
/// @dev equivalent to: abi.decode(params, (IV4Router.ExactOutputSingleParams))
function decodeSwapExactOutSingleParams(bytes calldata params)
internal
pure
returns (IV4Router.ExactOutputSingleParams calldata swapParams)
{
// ExactOutputSingleParams is a variable length struct so we just have to look up its location
assembly ("memory-safe") {
// only safety checks for the minimum length, where hookData is empty
// 0x140 = 10 * 0x20 -> 8 elements, bytes offset, and bytes length 0
if lt(params.length, 0x140) {
mstore(0, SLICE_ERROR_SELECTOR)
revert(0x1c, 4)
}
swapParams := add(params.offset, calldataload(params.offset))
}
}
/// @dev equivalent to: abi.decode(params, (Currency)) in calldata
function decodeCurrency(bytes calldata params) internal pure returns (Currency currency) {
assembly ("memory-safe") {
if lt(params.length, 0x20) {
mstore(0, SLICE_ERROR_SELECTOR)
revert(0x1c, 4)
}
currency := calldataload(params.offset)
}
}
/// @dev equivalent to: abi.decode(params, (Currency, Currency)) in calldata
function decodeCurrencyPair(bytes calldata params) internal pure returns (Currency currency0, Currency currency1) {
assembly ("memory-safe") {
if lt(params.length, 0x40) {
mstore(0, SLICE_ERROR_SELECTOR)
revert(0x1c, 4)
}
currency0 := calldataload(params.offset)
currency1 := calldataload(add(params.offset, 0x20))
}
}
/// @dev equivalent to: abi.decode(params, (Currency, Currency, address)) in calldata
function decodeCurrencyPairAndAddress(bytes calldata params)
internal
pure
returns (Currency currency0, Currency currency1, address _address)
{
assembly ("memory-safe") {
if lt(params.length, 0x60) {
mstore(0, SLICE_ERROR_SELECTOR)
revert(0x1c, 4)
}
currency0 := calldataload(params.offset)
currency1 := calldataload(add(params.offset, 0x20))
_address := calldataload(add(params.offset, 0x40))
}
}
/// @dev equivalent to: abi.decode(params, (Currency, address)) in calldata
function decodeCurrencyAndAddress(bytes calldata params)
internal
pure
returns (Currency currency, address _address)
{
assembly ("memory-safe") {
if lt(params.length, 0x40) {
mstore(0, SLICE_ERROR_SELECTOR)
revert(0x1c, 4)
}
currency := calldataload(params.offset)
_address := calldataload(add(params.offset, 0x20))
}
}
/// @dev equivalent to: abi.decode(params, (Currency, address, uint256)) in calldata
function decodeCurrencyAddressAndUint256(bytes calldata params)
internal
pure
returns (Currency currency, address _address, uint256 amount)
{
assembly ("memory-safe") {
if lt(params.length, 0x60) {
mstore(0, SLICE_ERROR_SELECTOR)
revert(0x1c, 4)
}
currency := calldataload(params.offset)
_address := calldataload(add(params.offset, 0x20))
amount := calldataload(add(params.offset, 0x40))
}
}
/// @dev equivalent to: abi.decode(params, (Currency, uint256)) in calldata
function decodeCurrencyAndUint256(bytes calldata params)
internal
pure
returns (Currency currency, uint256 amount)
{
assembly ("memory-safe") {
if lt(params.length, 0x40) {
mstore(0, SLICE_ERROR_SELECTOR)
revert(0x1c, 4)
}
currency := calldataload(params.offset)
amount := calldataload(add(params.offset, 0x20))
}
}
/// @dev equivalent to: abi.decode(params, (uint256)) in calldata
function decodeUint256(bytes calldata params) internal pure returns (uint256 amount) {
assembly ("memory-safe") {
if lt(params.length, 0x20) {
mstore(0, SLICE_ERROR_SELECTOR)
revert(0x1c, 4)
}
amount := calldataload(params.offset)
}
}
/// @dev equivalent to: abi.decode(params, (Currency, uint256, bool)) in calldata
function decodeCurrencyUint256AndBool(bytes calldata params)
internal
pure
returns (Currency currency, uint256 amount, bool boolean)
{
assembly ("memory-safe") {
if lt(params.length, 0x60) {
mstore(0, SLICE_ERROR_SELECTOR)
revert(0x1c, 4)
}
currency := calldataload(params.offset)
amount := calldataload(add(params.offset, 0x20))
boolean := calldataload(add(params.offset, 0x40))
}
}
/// @notice Decode the `_arg`-th element in `_bytes` as `bytes`
/// @param _bytes The input bytes string to extract a bytes string from
/// @param _arg The index of the argument to extract
function toBytes(bytes calldata _bytes, uint256 _arg) internal pure returns (bytes calldata res) {
uint256 length;
assembly ("memory-safe") {
// The offset of the `_arg`-th element is `32 * arg`, which stores the offset of the length pointer.
// shl(5, x) is equivalent to mul(32, x)
let lengthPtr :=
add(_bytes.offset, and(calldataload(add(_bytes.offset, shl(5, _arg))), OFFSET_OR_LENGTH_MASK))
// the number of bytes in the bytes string
length := and(calldataload(lengthPtr), OFFSET_OR_LENGTH_MASK)
// the offset where the bytes string begins
let offset := add(lengthPtr, 0x20)
// assign the return parameters
res.length := length
res.offset := offset
// if the provided bytes string isnt as long as the encoding says, revert
if lt(add(_bytes.length, _bytes.offset), add(length, offset)) {
mstore(0, SLICE_ERROR_SELECTOR)
revert(0x1c, 4)
}
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {Currency} from "./Currency.sol";
import {IHooks} from "../interfaces/IHooks.sol";
import {PoolIdLibrary} from "./PoolId.sol";
using PoolIdLibrary for PoolKey global;
/// @notice Returns the key for identifying a pool
struct PoolKey {
/// @notice The lower currency of the pool, sorted numerically
Currency currency0;
/// @notice The higher currency of the pool, sorted numerically
Currency currency1;
/// @notice The pool LP fee, capped at 1_000_000. If the highest bit is 1, the pool has a dynamic fee and must be exactly equal to 0x800000
uint24 fee;
/// @notice Ticks that involve positions must be a multiple of tick spacing
int24 tickSpacing;
/// @notice The hooks of the pool
IHooks hooks;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import {Currency} from "../types/Currency.sol";
import {PoolKey} from "../types/PoolKey.sol";
import {IHooks} from "./IHooks.sol";
import {IERC6909Claims} from "./external/IERC6909Claims.sol";
import {IProtocolFees} from "./IProtocolFees.sol";
import {BalanceDelta} from "../types/BalanceDelta.sol";
import {PoolId} from "../types/PoolId.sol";
import {IExtsload} from "./IExtsload.sol";
import {IExttload} from "./IExttload.sol";
/// @notice Interface for the PoolManager
interface IPoolManager is IProtocolFees, IERC6909Claims, IExtsload, IExttload {
/// @notice Thrown when a currency is not netted out after the contract is unlocked
error CurrencyNotSettled();
/// @notice Thrown when trying to interact with a non-initialized pool
error PoolNotInitialized();
/// @notice Thrown when unlock is called, but the contract is already unlocked
error AlreadyUnlocked();
/// @notice Thrown when a function is called that requires the contract to be unlocked, but it is not
error ManagerLocked();
/// @notice Pools are limited to type(int16).max tickSpacing in #initialize, to prevent overflow
error TickSpacingTooLarge(int24 tickSpacing);
/// @notice Pools must have a positive non-zero tickSpacing passed to #initialize
error TickSpacingTooSmall(int24 tickSpacing);
/// @notice PoolKey must have currencies where address(currency0) < address(currency1)
error CurrenciesOutOfOrderOrEqual(address currency0, address currency1);
/// @notice Thrown when a call to updateDynamicLPFee is made by an address that is not the hook,
/// or on a pool that does not have a dynamic swap fee.
error UnauthorizedDynamicLPFeeUpdate();
/// @notice Thrown when trying to swap amount of 0
error SwapAmountCannotBeZero();
///@notice Thrown when native currency is passed to a non native settlement
error NonzeroNativeValue();
/// @notice Thrown when `clear` is called with an amount that is not exactly equal to the open currency delta.
error MustClearExactPositiveDelta();
/// @notice Emitted when a new pool is initialized
/// @param id The abi encoded hash of the pool key struct for the new pool
/// @param currency0 The first currency of the pool by address sort order
/// @param currency1 The second currency of the pool by address sort order
/// @param fee The fee collected upon every swap in the pool, denominated in hundredths of a bip
/// @param tickSpacing The minimum number of ticks between initialized ticks
/// @param hooks The hooks contract address for the pool, or address(0) if none
/// @param sqrtPriceX96 The price of the pool on initialization
/// @param tick The initial tick of the pool corresponding to the initialized price
event Initialize(
PoolId indexed id,
Currency indexed currency0,
Currency indexed currency1,
uint24 fee,
int24 tickSpacing,
IHooks hooks,
uint160 sqrtPriceX96,
int24 tick
);
/// @notice Emitted when a liquidity position is modified
/// @param id The abi encoded hash of the pool key struct for the pool that was modified
/// @param sender The address that modified the pool
/// @param tickLower The lower tick of the position
/// @param tickUpper The upper tick of the position
/// @param liquidityDelta The amount of liquidity that was added or removed
/// @param salt The extra data to make positions unique
event ModifyLiquidity(
PoolId indexed id, address indexed sender, int24 tickLower, int24 tickUpper, int256 liquidityDelta, bytes32 salt
);
/// @notice Emitted for swaps between currency0 and currency1
/// @param id The abi encoded hash of the pool key struct for the pool that was modified
/// @param sender The address that initiated the swap call, and that received the callback
/// @param amount0 The delta of the currency0 balance of the pool
/// @param amount1 The delta of the currency1 balance of the pool
/// @param sqrtPriceX96 The sqrt(price) of the pool after the swap, as a Q64.96
/// @param liquidity The liquidity of the pool after the swap
/// @param tick The log base 1.0001 of the price of the pool after the swap
/// @param fee The swap fee in hundredths of a bip
event Swap(
PoolId indexed id,
address indexed sender,
int128 amount0,
int128 amount1,
uint160 sqrtPriceX96,
uint128 liquidity,
int24 tick,
uint24 fee
);
/// @notice Emitted for donations
/// @param id The abi encoded hash of the pool key struct for the pool that was donated to
/// @param sender The address that initiated the donate call
/// @param amount0 The amount donated in currency0
/// @param amount1 The amount donated in currency1
event Donate(PoolId indexed id, address indexed sender, uint256 amount0, uint256 amount1);
/// @notice All interactions on the contract that account deltas require unlocking. A caller that calls `unlock` must implement
/// `IUnlockCallback(msg.sender).unlockCallback(data)`, where they interact with the remaining functions on this contract.
/// @dev The only functions callable without an unlocking are `initialize` and `updateDynamicLPFee`
/// @param data Any data to pass to the callback, via `IUnlockCallback(msg.sender).unlockCallback(data)`
/// @return The data returned by the call to `IUnlockCallback(msg.sender).unlockCallback(data)`
function unlock(bytes calldata data) external returns (bytes memory);
/// @notice Initialize the state for a given pool ID
/// @dev A swap fee totaling MAX_SWAP_FEE (100%) makes exact output swaps impossible since the input is entirely consumed by the fee
/// @param key The pool key for the pool to initialize
/// @param sqrtPriceX96 The initial square root price
/// @return tick The initial tick of the pool
function initialize(PoolKey memory key, uint160 sqrtPriceX96) external returns (int24 tick);
struct ModifyLiquidityParams {
// the lower and upper tick of the position
int24 tickLower;
int24 tickUpper;
// how to modify the liquidity
int256 liquidityDelta;
// a value to set if you want unique liquidity positions at the same range
bytes32 salt;
}
/// @notice Modify the liquidity for the given pool
/// @dev Poke by calling with a zero liquidityDelta
/// @param key The pool to modify liquidity in
/// @param params The parameters for modifying the liquidity
/// @param hookData The data to pass through to the add/removeLiquidity hooks
/// @return callerDelta The balance delta of the caller of modifyLiquidity. This is the total of both principal, fee deltas, and hook deltas if applicable
/// @return feesAccrued The balance delta of the fees generated in the liquidity range. Returned for informational purposes
/// @dev Note that feesAccrued can be artificially inflated by a malicious actor and integrators should be careful using the value
/// For pools with a single liquidity position, actors can donate to themselves to inflate feeGrowthGlobal (and consequently feesAccrued)
/// atomically donating and collecting fees in the same unlockCallback may make the inflated value more extreme
function modifyLiquidity(PoolKey memory key, ModifyLiquidityParams memory params, bytes calldata hookData)
external
returns (BalanceDelta callerDelta, BalanceDelta feesAccrued);
struct SwapParams {
/// Whether to swap token0 for token1 or vice versa
bool zeroForOne;
/// The desired input amount if negative (exactIn), or the desired output amount if positive (exactOut)
int256 amountSpecified;
/// The sqrt price at which, if reached, the swap will stop executing
uint160 sqrtPriceLimitX96;
}
/// @notice Swap against the given pool
/// @param key The pool to swap in
/// @param params The parameters for swapping
/// @param hookData The data to pass through to the swap hooks
/// @return swapDelta The balance delta of the address swapping
/// @dev Swapping on low liquidity pools may cause unexpected swap amounts when liquidity available is less than amountSpecified.
/// Additionally note that if interacting with hooks that have the BEFORE_SWAP_RETURNS_DELTA_FLAG or AFTER_SWAP_RETURNS_DELTA_FLAG
/// the hook may alter the swap input/output. Integrators should perform checks on the returned swapDelta.
function swap(PoolKey memory key, SwapParams memory params, bytes calldata hookData)
external
returns (BalanceDelta swapDelta);
/// @notice Donate the given currency amounts to the in-range liquidity providers of a pool
/// @dev Calls to donate can be frontrun adding just-in-time liquidity, with the aim of receiving a portion donated funds.
/// Donors should keep this in mind when designing donation mechanisms.
/// @dev This function donates to in-range LPs at slot0.tick. In certain edge-cases of the swap algorithm, the `sqrtPrice` of
/// a pool can be at the lower boundary of tick `n`, but the `slot0.tick` of the pool is already `n - 1`. In this case a call to
/// `donate` would donate to tick `n - 1` (slot0.tick) not tick `n` (getTickAtSqrtPrice(slot0.sqrtPriceX96)).
/// Read the comments in `Pool.swap()` for more information about this.
/// @param key The key of the pool to donate to
/// @param amount0 The amount of currency0 to donate
/// @param amount1 The amount of currency1 to donate
/// @param hookData The data to pass through to the donate hooks
/// @return BalanceDelta The delta of the caller after the donate
function donate(PoolKey memory key, uint256 amount0, uint256 amount1, bytes calldata hookData)
external
returns (BalanceDelta);
/// @notice Writes the current ERC20 balance of the specified currency to transient storage
/// This is used to checkpoint balances for the manager and derive deltas for the caller.
/// @dev This MUST be called before any ERC20 tokens are sent into the contract, but can be skipped
/// for native tokens because the amount to settle is determined by the sent value.
/// However, if an ERC20 token has been synced and not settled, and the caller instead wants to settle
/// native funds, this function can be called with the native currency to then be able to settle the native currency
function sync(Currency currency) external;
/// @notice Called by the user to net out some value owed to the user
/// @dev Will revert if the requested amount is not available, consider using `mint` instead
/// @dev Can also be used as a mechanism for free flash loans
/// @param currency The currency to withdraw from the pool manager
/// @param to The address to withdraw to
/// @param amount The amount of currency to withdraw
function take(Currency currency, address to, uint256 amount) external;
/// @notice Called by the user to pay what is owed
/// @return paid The amount of currency settled
function settle() external payable returns (uint256 paid);
/// @notice Called by the user to pay on behalf of another address
/// @param recipient The address to credit for the payment
/// @return paid The amount of currency settled
function settleFor(address recipient) external payable returns (uint256 paid);
/// @notice WARNING - Any currency that is cleared, will be non-retrievable, and locked in the contract permanently.
/// A call to clear will zero out a positive balance WITHOUT a corresponding transfer.
/// @dev This could be used to clear a balance that is considered dust.
/// Additionally, the amount must be the exact positive balance. This is to enforce that the caller is aware of the amount being cleared.
function clear(Currency currency, uint256 amount) external;
/// @notice Called by the user to move value into ERC6909 balance
/// @param to The address to mint the tokens to
/// @param id The currency address to mint to ERC6909s, as a uint256
/// @param amount The amount of currency to mint
/// @dev The id is converted to a uint160 to correspond to a currency address
/// If the upper 12 bytes are not 0, they will be 0-ed out
function mint(address to, uint256 id, uint256 amount) external;
/// @notice Called by the user to move value from ERC6909 balance
/// @param from The address to burn the tokens from
/// @param id The currency address to burn from ERC6909s, as a uint256
/// @param amount The amount of currency to burn
/// @dev The id is converted to a uint160 to correspond to a currency address
/// If the upper 12 bytes are not 0, they will be 0-ed out
function burn(address from, uint256 id, uint256 amount) external;
/// @notice Updates the pools lp fees for the a pool that has enabled dynamic lp fees.
/// @dev A swap fee totaling MAX_SWAP_FEE (100%) makes exact output swaps impossible since the input is entirely consumed by the fee
/// @param key The key of the pool to update dynamic LP fees for
/// @param newDynamicLPFee The new dynamic pool LP fee
function updateDynamicLPFee(PoolKey memory key, uint24 newDynamicLPFee) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {IPostDispatchHook} from '@hyperlane/core/contracts/interfaces/hooks/IPostDispatchHook.sol';
interface IInterchainAccountRouter {
/// @notice Dispatches a commitment and reveal message to the destination domain.
/// Useful for when we want to keep calldata secret (e.g. when executing a swap)
/// @dev The commitment message is dispatched first, followed by the reveal message.
/// The revealed calldata is executed by the `revealAndExecute` function, which will be called the OffChainLookupIsm in its `verify` function.
/// @param _destination The remote domain of the chain to make calls on
/// @param _router The remote router address
/// @param _ism The remote ISM address
/// @param _hookMetadata The hook metadata to override with for the hook set by the owner
/// @param _salt Salt which allows control over account derivation.
/// @param _hook The hook to use after sending our message to the mailbox
/// @param _commitment The commitment to dispatch
/// @return _commitmentMsgId The Hyperlane message ID of the commitment message
/// @return _revealMsgId The Hyperlane message ID of the reveal message
function callRemoteCommitReveal(
uint32 _destination,
bytes32 _router,
bytes32 _ism,
bytes memory _hookMetadata,
IPostDispatchHook _hook,
bytes32 _salt,
bytes32 _commitment
) external payable returns (bytes32 _commitmentMsgId, bytes32 _revealMsgId);
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.24;
import {ERC20} from 'solmate/src/tokens/ERC20.sol';
import {RouterImmutables} from '../RouterImmutables.sol';
import {IPool} from '../../../interfaces/external/IPool.sol';
import {IPoolFactory} from '../../../interfaces/external/IPoolFactory.sol';
import {Permit2Payments} from '../../Permit2Payments.sol';
import {Constants} from '../../../libraries/Constants.sol';
import {UniswapV2Library} from './UniswapV2Library.sol';
import {V2Path} from './V2Path.sol';
/// @title Router for Trades on V2 Pools
abstract contract V2SwapRouter is RouterImmutables, Permit2Payments {
using V2Path for bytes;
error V2TooLittleReceived();
error V2TooMuchRequested();
error V2InvalidPath();
error InvalidPath();
/// @notice Calculates the v2 address for a pair without making any external calls
/// @param isUni Whether this is a Uniswap V2 path or not
/// @param path The encoded token0, token1 (and stable)
/// @return pair The resultant v2 pair address
function pairFor(bool isUni, bytes calldata path) internal view returns (address pair) {
address token0;
address token1;
address tokenA;
address tokenB;
bytes32 salt;
address factory;
bytes32 initCodeHash;
if (isUni) {
factory = UNISWAP_V2_FACTORY;
initCodeHash = UNISWAP_V2_PAIR_INIT_CODE_HASH;
(tokenA, tokenB) = path.v2DecodePair();
(token0, token1) = UniswapV2Library.sortTokens({tokenA: tokenA, tokenB: tokenB});
salt = keccak256(abi.encodePacked(token0, token1));
} else {
factory = VELODROME_V2_FACTORY;
initCodeHash = VELODROME_V2_INIT_CODE_HASH;
bool stable;
(tokenA, tokenB, stable) = path.decodeRoute();
(token0, token1) = UniswapV2Library.sortTokens({tokenA: tokenA, tokenB: tokenB});
salt = keccak256(abi.encodePacked(token0, token1, stable));
}
pair = UniswapV2Library.pairForSalt({factory: factory, initCodeHash: initCodeHash, salt: salt});
}
/// @notice Calculates the v2 address for a pair and the pair's token0
/// @param isUni Whether this is a Uniswap V2 path or not
/// @param path The encoded token0, token1 (and stable)
/// @return pair The resultant v2 pair address
/// @return token0 The token considered token0 in this pair
function pairAndToken0For(bool isUni, bytes calldata path) internal view returns (address pair, address token0) {
(address tokenA, address tokenB) = isUni ? path.v2DecodePair() : path.veloDecodePair();
(token0,) = UniswapV2Library.sortTokens({tokenA: tokenA, tokenB: tokenB});
pair = pairFor({isUni: isUni, path: path});
}
/// @notice Calculates the v2 address for a pair and fetches the reserves for each token
/// @param isUni Whether this is a Uniswap V2 path or not
/// @param path The encoded token0, token1 (and stable)
/// @return pair The resultant v2 pair address
/// @return reserveA The reserves for tokenA
/// @return reserveB The reserves for tokenB
function pairAndReservesFor(bool isUni, bytes calldata path)
internal
view
returns (address pair, uint256 reserveA, uint256 reserveB)
{
address token0;
(pair, token0) = pairAndToken0For({isUni: isUni, path: path});
(uint256 reserve0, uint256 reserve1,) = IPool(pair).getReserves();
(reserveA, reserveB) = path.decodeFirstToken() == token0 ? (reserve0, reserve1) : (reserve1, reserve0);
}
/// @notice Returns the input amount needed for a desired output amount in a multi-hop trade
/// @param amountOut The desired output amount
/// @param path The path of the multi-hop trade
/// @param isUni Whether this is a Uniswap V2 path or not
/// @return amount The input amount of the input token
/// @return pair The first pair in the trade
function getAmountInMultihop(uint256 amountOut, bytes calldata path, bool isUni)
internal
view
returns (uint256 amount, address pair)
{
if (isUni ? !path.hasMultipleTokens() : !path.hasMultipleRoutes()) revert InvalidPath();
amount = amountOut;
uint256 reserveIn;
uint256 reserveOut;
if (isUni) {
while (path.hasMultipleTokens()) {
(pair, reserveIn, reserveOut) = pairAndReservesFor({isUni: true, path: path.v2GetLastTokens()});
amount = UniswapV2Library.getAmountIn({
fee: Constants.V2_FEE,
amountOut: amount,
reserveIn: reserveIn,
reserveOut: reserveOut,
isUni: isUni,
stable: false
});
path = path.v2RemoveLastToken();
}
} else {
while (path.hasMultipleRoutes()) {
bytes calldata veloPath = path.veloGetLastRoute();
bool stable = veloPath.getFirstStable();
(pair, reserveIn, reserveOut) = pairAndReservesFor({isUni: false, path: veloPath});
amount = UniswapV2Library.getAmountIn({
fee: IPoolFactory(VELODROME_V2_FACTORY).getFee({_pool: pair, _stable: stable}),
amountOut: amount,
reserveIn: reserveIn,
reserveOut: reserveOut,
isUni: isUni,
stable: stable
});
path = path.veloRemoveLastRoute();
}
}
}
/// @notice Performs a V2 exact input swap
/// @param recipient The recipient of the output tokens
/// @param amountIn The amount of input tokens for the trade
/// @param amountOutMinimum The minimum desired amount of output tokens
/// @param path The path of the trade as a bytes string
/// @param payer The address that will be paying the input
/// @param isUni Whether this is a Uniswap V2 path or not
function v2SwapExactInput(
address recipient,
uint256 amountIn,
uint256 amountOutMinimum,
bytes calldata path,
address payer,
bool isUni
) internal {
ERC20 tokenOut = ERC20(path.getTokenOut());
address firstPair = isUni
? pairFor({isUni: true, path: path.v2GetFirstTokens()})
: pairFor({isUni: false, path: path.getFirstRoute()});
if (
amountIn != Constants.ALREADY_PAID // amountIn of 0 to signal that the pair already has the tokens
) {
payOrPermit2Transfer({token: path.decodeFirstToken(), payer: payer, recipient: firstPair, amount: amountIn});
}
uint256 balanceBefore = tokenOut.balanceOf(recipient);
isUni
? _v2Swap({path: path, recipient: recipient, pair: firstPair})
: _veloSwap({routes: path, recipient: recipient, pair: firstPair});
uint256 amountOut = tokenOut.balanceOf(recipient) - balanceBefore;
if (amountOut < amountOutMinimum) revert V2TooLittleReceived();
}
/// @notice Performs a V2 exact output swap
/// @param recipient The recipient of the output tokens
/// @param amountOut The amount of output tokens to receive for the trade
/// @param amountInMaximum The maximum desired amount of input tokens
/// @param path The path of the trade as a bytes string
/// @param payer The address that will be paying the input
/// @param isUni Whether this is a Uniswap V2 path or not
function v2SwapExactOutput(
address recipient,
uint256 amountOut,
uint256 amountInMaximum,
bytes calldata path,
address payer,
bool isUni
) internal {
(uint256 amountIn, address firstPair) = getAmountInMultihop({amountOut: amountOut, path: path, isUni: isUni});
if (amountIn > amountInMaximum) revert V2TooMuchRequested();
payOrPermit2Transfer({token: path.decodeFirstToken(), payer: payer, recipient: firstPair, amount: amountIn});
isUni
? _v2Swap({path: path, recipient: recipient, pair: firstPair})
: _veloSwap({routes: path, recipient: recipient, pair: firstPair});
}
/// @dev path only contains addresses
function _v2Swap(bytes calldata path, address recipient, address pair) private {
unchecked {
if (!path.hasMultipleTokens()) revert V2InvalidPath();
// cached to save on duplicate operations
(address token0, address token1) = path.v2DecodePair();
(token0,) = UniswapV2Library.sortTokens({tokenA: token0, tokenB: token1});
uint256 finalPairIndex = path.v2Length() - 1;
uint256 penultimatePairIndex = finalPairIndex - 1;
for (uint256 i; i < finalPairIndex; i++) {
(address input,) = (path.pairAt(i).v2DecodePair());
(uint256 reserve0, uint256 reserve1,) = IPool(pair).getReserves();
(uint256 reserveInput, uint256 reserveOutput) =
input == token0 ? (reserve0, reserve1) : (reserve1, reserve0);
uint256 amountInput = ERC20(input).balanceOf(pair) - reserveInput;
uint256 amountOutput = UniswapV2Library.getAmountOut({
amountIn: amountInput,
reserveIn: reserveInput,
reserveOut: reserveOutput
});
(uint256 amount0Out, uint256 amount1Out) =
input == token0 ? (uint256(0), amountOutput) : (amountOutput, uint256(0));
address nextPair;
(nextPair, token0) = i < penultimatePairIndex
? pairAndToken0For({isUni: true, path: path.pairAt(i + 1)})
: (recipient, address(0));
IPool(pair).swap({amount0Out: amount0Out, amount1Out: amount1Out, to: nextPair, data: new bytes(0)});
pair = nextPair;
}
}
}
/// @dev path contains token addresses and stable boolean
function _veloSwap(bytes calldata routes, address recipient, address pair) private {
unchecked {
uint256 length = routes.veloLength();
if (length == 0) revert V2InvalidPath();
// cached to save on duplicate operations
(address input, address output, bool stable) = routes.veloRouteAt(0).decodeRoute();
(address token0,) = UniswapV2Library.sortTokens({tokenA: input, tokenB: output});
uint256 finalPairIndex = length - 1;
for (uint256 i; i < length; i++) {
(input, output, stable) = routes.veloRouteAt(i).decodeRoute();
(uint256 reserve0, uint256 reserve1,) = IPool(pair).getReserves();
(uint256 reserveInput, uint256 reserveOutput) =
input == token0 ? (reserve0, reserve1) : (reserve1, reserve0);
uint256 amountInput = ERC20(input).balanceOf(pair) - reserveInput;
uint256 amountOutput = UniswapV2Library.getAmountOut({
factory: VELODROME_V2_FACTORY,
pair: pair,
amountIn: amountInput,
reserveIn: reserveInput,
reserveOut: reserveOutput,
from: input,
to: output,
stable: stable
});
(uint256 amount0Out, uint256 amount1Out) =
input == token0 ? (uint256(0), amountOutput) : (amountOutput, uint256(0));
address nextPair;
(nextPair, token0) = i < finalPairIndex
? pairAndToken0For({isUni: false, path: routes.veloRouteAt(i + 1)})
: (recipient, address(0));
IPool(pair).swap({amount0Out: amount0Out, amount1Out: amount1Out, to: nextPair, data: new bytes(0)});
pair = nextPair;
}
}
}
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.24;
import {IUniswapV3SwapCallback} from '@uniswap/v3-core/contracts/interfaces/callback/IUniswapV3SwapCallback.sol';
import {ActionConstants} from '@uniswap/v4-periphery/src/libraries/ActionConstants.sol';
import {CalldataDecoder} from '@uniswap/v4-periphery/src/libraries/CalldataDecoder.sol';
import {SafeCast} from '@uniswap/v3-core/contracts/libraries/SafeCast.sol';
import {ERC20} from 'solmate/src/tokens/ERC20.sol';
import {ICLPool} from '../../../interfaces/external/ICLPool.sol';
import {RouterImmutables} from '../RouterImmutables.sol';
import {Permit2Payments} from '../../Permit2Payments.sol';
import {MaxInputAmount} from '../../../libraries/MaxInputAmount.sol';
import {BytesLib} from './BytesLib.sol';
import {V3Path} from './V3Path.sol';
/// @title Router for Trades on Concentrated Liquidity pools
abstract contract V3SwapRouter is RouterImmutables, Permit2Payments, IUniswapV3SwapCallback {
using V3Path for bytes;
using BytesLib for bytes;
using CalldataDecoder for bytes;
using SafeCast for uint256;
error V3InvalidSwap();
error V3TooLittleReceived();
error V3TooMuchRequested();
error V3InvalidAmountOut();
error V3InvalidCaller();
/// @dev The minimum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MIN_TICK)
uint160 internal constant MIN_SQRT_RATIO = 4295128739;
/// @dev The maximum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MAX_TICK)
uint160 internal constant MAX_SQRT_RATIO = 1461446703485210103287273052203988822378723970342;
function uniswapV3SwapCallback(int256 amount0Delta, int256 amount1Delta, bytes calldata data) external {
if (amount0Delta <= 0 && amount1Delta <= 0) revert V3InvalidSwap(); // swaps entirely within 0-liquidity regions are not supported
(, address payer, bool isUni) = abi.decode(data, (bytes, address, bool));
bytes calldata path = data.toBytes(0);
// because exact output swaps are executed in reverse order, in this case tokenOut is actually tokenIn
(address tokenIn, uint24 poolParam, address tokenOut) = path.decodeFirstPool();
if (computePoolAddress({tokenA: tokenIn, tokenB: tokenOut, poolParam: poolParam, isUni: isUni}) != msg.sender) {
revert V3InvalidCaller();
}
(bool isExactInput, uint256 amountToPay) =
amount0Delta > 0 ? (tokenIn < tokenOut, uint256(amount0Delta)) : (tokenOut < tokenIn, uint256(amount1Delta));
if (isExactInput) {
// Pay the pool (msg.sender)
payOrPermit2Transfer(tokenIn, payer, msg.sender, amountToPay);
} else {
// either initiate the next swap or pay
if (path.hasMultiplePools()) {
// this is an intermediate step so the payer is actually this contract
path = path.skipToken();
_swap({
amount: -amountToPay.toInt256(),
recipient: msg.sender,
path: path,
payer: payer,
isExactIn: false,
isUni: isUni
});
} else {
if (amountToPay > MaxInputAmount.get()) revert V3TooMuchRequested();
// note that because exact output swaps are executed in reverse order, tokenOut is actually tokenIn
payOrPermit2Transfer(tokenOut, payer, msg.sender, amountToPay);
}
}
}
/// @notice Performs an exact input swap on a concentrated liquidity pool
/// @param recipient The recipient of the output tokens
/// @param amountIn The amount of input tokens for the trade
/// @param amountOutMinimum The minimum desired amount of output tokens
/// @param path The path of the trade as a bytes string
/// @param payer The address that will be paying the input
function v3SwapExactInput(
address recipient,
uint256 amountIn,
uint256 amountOutMinimum,
bytes calldata path,
address payer,
bool isUni
) internal {
// use amountIn == ActionConstants.CONTRACT_BALANCE as a flag to swap the entire balance of the contract
if (amountIn == ActionConstants.CONTRACT_BALANCE) {
address tokenIn = path.decodeFirstToken();
amountIn = ERC20(tokenIn).balanceOf(address(this));
}
uint256 amountOut;
while (true) {
bool hasMultiplePools = path.hasMultiplePools();
// the outputs of prior swaps become the inputs to subsequent ones
(int256 amount0Delta, int256 amount1Delta, bool zeroForOne) = _swap({
amount: amountIn.toInt256(),
recipient: hasMultiplePools ? address(this) : recipient, // for intermediate swaps, this contract custodies
path: path.getFirstPool(), // only the first pool is needed
payer: payer, // for intermediate swaps, this contract custodies
isExactIn: true,
isUni: isUni
});
amountIn = uint256(-(zeroForOne ? amount1Delta : amount0Delta));
// decide whether to continue or terminate
if (hasMultiplePools) {
payer = address(this);
path = path.skipToken();
} else {
amountOut = amountIn;
break;
}
}
if (amountOut < amountOutMinimum) revert V3TooLittleReceived();
}
/// @notice Performs an exact output swap on a concentrated liquidity pool
/// @param recipient The recipient of the output tokens
/// @param amountOut The amount of output tokens to receive for the trade
/// @param amountInMaximum The maximum desired amount of input tokens
/// @param path The path of the trade as a bytes string
/// @param payer The address that will be paying the input
function v3SwapExactOutput(
address recipient,
uint256 amountOut,
uint256 amountInMaximum,
bytes calldata path,
address payer,
bool isUni
) internal {
MaxInputAmount.set(amountInMaximum);
(int256 amount0Delta, int256 amount1Delta, bool zeroForOne) = _swap({
amount: -amountOut.toInt256(),
recipient: recipient,
path: path,
payer: payer,
isExactIn: false,
isUni: isUni
});
uint256 amountOutReceived = zeroForOne ? uint256(-amount1Delta) : uint256(-amount0Delta);
if (amountOutReceived != amountOut) revert V3InvalidAmountOut();
MaxInputAmount.set(0);
}
/// @dev Performs a single swap for both exactIn and exactOut
/// For exactIn, `amount` is `amountIn`. For exactOut, `amount` is `-amountOut`
function _swap(int256 amount, address recipient, bytes calldata path, address payer, bool isExactIn, bool isUni)
private
returns (int256 amount0Delta, int256 amount1Delta, bool zeroForOne)
{
(address tokenIn, uint24 poolParam, address tokenOut) = path.decodeFirstPool();
zeroForOne = isExactIn ? tokenIn < tokenOut : tokenOut < tokenIn;
(amount0Delta, amount1Delta) = ICLPool(computePoolAddress(tokenIn, tokenOut, poolParam, isUni)).swap(
recipient,
zeroForOne,
amount,
(zeroForOne ? MIN_SQRT_RATIO + 1 : MAX_SQRT_RATIO - 1),
abi.encode(path, payer, isUni)
);
}
/// @dev `poolParam` is `tickSpacing` in Slipstream pools and `fee` in UniV3 pools.
function computePoolAddress(address tokenA, address tokenB, uint24 poolParam, bool isUni)
private
view
returns (address pool)
{
if (tokenA > tokenB) (tokenA, tokenB) = (tokenB, tokenA);
(address factory, bytes32 initCodeHash) = isUni
? (UNISWAP_V3_FACTORY, UNISWAP_V3_POOL_INIT_CODE_HASH)
: (VELODROME_CL_FACTORY, VELODROME_CL_POOL_INIT_CODE_HASH);
pool = address(
uint160(
uint256(
keccak256(
abi.encodePacked(
hex'ff', factory, keccak256(abi.encode(tokenA, tokenB, poolParam)), initCodeHash
)
)
)
)
);
}
}// SPDX-License-Identifier: GPL-3.0-or-later
/// @title Library for Bytes Manipulation
pragma solidity ^0.8.0;
import {Constants} from '../../../libraries/Constants.sol';
import {CalldataDecoder} from '@uniswap/v4-periphery/src/libraries/CalldataDecoder.sol';
library BytesLib {
using CalldataDecoder for bytes;
error SliceOutOfBounds();
/// @notice Returns the address starting at byte 0
/// @dev length and overflow checks must be carried out before calling
/// @param _bytes The input bytes string to slice
/// @return _address The address starting at byte 0
function toAddress(bytes calldata _bytes) internal pure returns (address _address) {
if (_bytes.length < Constants.ADDR_SIZE) revert SliceOutOfBounds();
assembly {
_address := shr(96, calldataload(_bytes.offset))
}
}
/// @notice Returns the pool details starting at byte 0
/// @dev length and overflow checks must be carried out before calling
/// @param _bytes The input bytes string to slice
/// @return token0 The address at byte 0
/// @return poolParam The uint24 value starting at byte 20
/// @return token1 The address at byte 23
function toPool(bytes calldata _bytes) internal pure returns (address token0, uint24 poolParam, address token1) {
if (_bytes.length < Constants.V3_POP_OFFSET) revert SliceOutOfBounds();
assembly {
let firstWord := calldataload(_bytes.offset)
token0 := shr(96, firstWord)
poolParam := and(shr(72, firstWord), 0xffffff)
token1 := shr(96, calldataload(add(_bytes.offset, 23)))
}
}
/// @notice Decode the `_arg`-th element in `_bytes` as a dynamic array
/// @dev The decoding of `length` and `offset` is universal,
/// whereas the type declaration of `res` instructs the compiler how to read it.
/// @param _bytes The input bytes string to slice
/// @param _arg The index of the argument to extract
/// @return length Length of the array
/// @return offset Pointer to the data part of the array
function toLengthOffset(bytes calldata _bytes, uint256 _arg)
internal
pure
returns (uint256 length, uint256 offset)
{
uint256 relativeOffset;
assembly {
// The offset of the `_arg`-th element is `32 * arg`, which stores the offset of the length pointer.
// shl(5, x) is equivalent to mul(32, x)
let lengthPtr := add(_bytes.offset, calldataload(add(_bytes.offset, shl(5, _arg))))
length := calldataload(lengthPtr)
offset := add(lengthPtr, 0x20)
relativeOffset := sub(offset, _bytes.offset)
}
if (_bytes.length < length + relativeOffset) revert SliceOutOfBounds();
}
/// @notice Equivalent to abi.decode(bytes, bytes[])
/// @param _bytes The input bytes string to extract an parameters from
function decodeCommandsAndInputs(bytes calldata _bytes) internal pure returns (bytes calldata, bytes[] calldata) {
return _bytes.decodeActionsRouterParams();
}
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.24;
import {Constants} from '../libraries/Constants.sol';
import {ActionConstants} from '@uniswap/v4-periphery/src/libraries/ActionConstants.sol';
import {BipsLibrary} from '@uniswap/v4-periphery/src/libraries/BipsLibrary.sol';
import {PaymentsImmutables} from '../modules/PaymentsImmutables.sol';
import {SafeTransferLib} from 'solmate/src/utils/SafeTransferLib.sol';
import {ERC20} from 'solmate/src/tokens/ERC20.sol';
/// @title Payments contract
/// @notice Performs various operations around the payment of ETH and tokens
abstract contract Payments is PaymentsImmutables {
using SafeTransferLib for ERC20;
using SafeTransferLib for address;
using BipsLibrary for uint256;
error InsufficientToken();
error InsufficientETH();
/// @notice Pays an amount of ETH or ERC20 to a recipient
/// @param token The token to pay (can be ETH using Constants.ETH)
/// @param recipient The address that will receive the payment
/// @param value The amount to pay
function pay(address token, address recipient, uint256 value) internal {
if (token == Constants.ETH) {
recipient.safeTransferETH(value);
} else {
if (value == ActionConstants.CONTRACT_BALANCE) {
value = ERC20(token).balanceOf(address(this));
}
ERC20(token).safeTransfer(recipient, value);
}
}
/// @notice Pays a proportion of the contract's ETH or ERC20 to a recipient
/// @param token The token to pay (can be ETH using Constants.ETH)
/// @param recipient The address that will receive payment
/// @param bips Portion in bips of whole balance of the contract
function payPortion(address token, address recipient, uint256 bips) internal {
if (token == Constants.ETH) {
uint256 balance = address(this).balance;
uint256 amount = balance.calculatePortion(bips);
recipient.safeTransferETH(amount);
} else {
uint256 balance = ERC20(token).balanceOf(address(this));
uint256 amount = balance.calculatePortion(bips);
ERC20(token).safeTransfer(recipient, amount);
}
}
/// @notice Sweeps all of the contract's ERC20 or ETH to an address
/// @param token The token to sweep (can be ETH using Constants.ETH)
/// @param recipient The address that will receive payment
/// @param amountMinimum The minimum desired amount
function sweep(address token, address recipient, uint256 amountMinimum) internal {
uint256 balance;
if (token == Constants.ETH) {
balance = address(this).balance;
if (balance < amountMinimum) revert InsufficientETH();
if (balance > 0) recipient.safeTransferETH(balance);
} else {
balance = ERC20(token).balanceOf(address(this));
if (balance < amountMinimum) revert InsufficientToken();
if (balance > 0) ERC20(token).safeTransfer(recipient, balance);
}
}
/// @notice Wraps an amount of ETH into WETH
/// @param recipient The recipient of the WETH
/// @param amount The amount to wrap (can be CONTRACT_BALANCE)
function wrapETH(address recipient, uint256 amount) internal {
if (amount == ActionConstants.CONTRACT_BALANCE) {
amount = address(this).balance;
} else if (amount > address(this).balance) {
revert InsufficientETH();
}
if (amount > 0) {
WETH9.deposit{value: amount}();
if (recipient != address(this)) {
WETH9.transfer(recipient, amount);
}
}
}
/// @notice Unwraps all of the contract's WETH into ETH
/// @param recipient The recipient of the ETH
/// @param amountMinimum The minimum amount of ETH desired
function unwrapWETH9(address recipient, uint256 amountMinimum) internal {
uint256 value = WETH9.balanceOf(address(this));
if (value < amountMinimum) {
revert InsufficientETH();
}
if (value > 0) {
WETH9.withdraw(value);
if (recipient != address(this)) {
recipient.safeTransferETH(value);
}
}
}
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.24;
import {ERC20} from 'solmate/src/tokens/ERC20.sol';
import {SafeTransferLib} from 'solmate/src/utils/SafeTransferLib.sol';
import {HypXERC20} from '@hyperlane/core/contracts/token/extensions/HypXERC20.sol';
import {StandardHookMetadata} from '@hyperlane/core/contracts/hooks/libs/StandardHookMetadata.sol';
import {TypeCasts} from '@hyperlane/core/contracts/libs/TypeCasts.sol';
import {ITokenBridge} from '../../interfaces/external/ITokenBridge.sol';
import {BridgeTypes} from '../../libraries/BridgeTypes.sol';
import {Permit2Payments} from './../Permit2Payments.sol';
/// @title BridgeRouter
/// @notice Handles cross-chain bridging operations
abstract contract BridgeRouter is Permit2Payments {
using SafeTransferLib for ERC20;
error InvalidTokenAddress();
error InvalidRecipient();
error InvalidBridgeType(uint8 bridgeType);
uint256 public constant OPTIMISM_CHAIN_ID = 10;
/// @notice Send tokens x-chain using the selected bridge
/// @param bridgeType The type of bridge to use
/// @param sender The address initiating the bridge
/// @param recipient The recipient address on the destination chain
/// @param token The token to be bridged
/// @param bridge The bridge used for the token
/// @param amount The amount to bridge
/// @param msgFee The fee to pay for token bridging
/// @param domain The destination domain
/// @param payer The address to pay for the transfer
function bridgeToken(
uint8 bridgeType,
address sender,
address recipient,
address token,
address bridge,
uint256 amount,
uint256 msgFee,
uint32 domain,
address payer
) internal {
if (recipient == address(0)) revert InvalidRecipient();
if (bridgeType == BridgeTypes.HYP_XERC20) {
if (address(HypXERC20(bridge).wrappedToken()) != token) revert InvalidTokenAddress();
prepareTokensForBridge({_token: token, _bridge: bridge, _payer: payer, _amount: amount});
executeHypXERC20Bridge({
bridge: bridge,
sender: sender,
recipient: recipient,
amount: amount,
msgFee: msgFee,
domain: domain
});
ERC20(token).safeApprove({to: bridge, amount: 0});
} else if (bridgeType == BridgeTypes.XVELO) {
address _bridgeToken =
block.chainid == OPTIMISM_CHAIN_ID ? ITokenBridge(bridge).erc20() : ITokenBridge(bridge).xerc20();
if (_bridgeToken != token) revert InvalidTokenAddress();
prepareTokensForBridge({_token: token, _bridge: bridge, _payer: payer, _amount: amount});
executeXVELOBridge({
bridge: bridge,
sender: sender,
recipient: recipient,
amount: amount,
msgFee: msgFee,
domain: domain
});
ERC20(token).safeApprove({to: bridge, amount: 0});
} else {
revert InvalidBridgeType({bridgeType: bridgeType});
}
}
/// @dev Executes bridge transfer via HypXERC20
function executeHypXERC20Bridge(
address bridge,
address sender,
address recipient,
uint256 amount,
uint256 msgFee,
uint32 domain
) private {
bytes memory metadata = StandardHookMetadata.formatMetadata({
_msgValue: uint256(0),
_gasLimit: HypXERC20(bridge).destinationGas(domain),
_refundAddress: sender,
_customMetadata: ''
});
HypXERC20(bridge).transferRemote{value: msgFee}({
_destination: domain,
_recipient: TypeCasts.addressToBytes32(recipient),
_amountOrId: amount,
_hookMetadata: metadata,
_hook: address(HypXERC20(bridge).hook())
});
}
/// @dev Executes bridge transfer via XVELO TokenBridge
function executeXVELOBridge(
address bridge,
address sender,
address recipient,
uint256 amount,
uint256 msgFee,
uint32 domain
) private {
ITokenBridge(bridge).sendToken{value: msgFee}({
_recipient: recipient,
_amount: amount,
_domain: domain,
_refundAddress: sender
});
}
/// @dev Moves the tokens from sender to this contract then approves the bridge
function prepareTokensForBridge(address _token, address _bridge, address _payer, uint256 _amount) private {
if (_payer != address(this)) {
payOrPermit2Transfer({token: _token, payer: _payer, recipient: address(this), amount: _amount});
}
ERC20(_token).safeApprove({to: _bridge, amount: _amount});
}
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.24;
import {ActionConstants} from '@uniswap/v4-periphery/src/libraries/ActionConstants.sol';
/// @title Constant state
/// @notice Constant state used by the Universal Router
library Constants {
/// @dev Used for identifying cases when a v2 pair has already received input tokens
uint256 internal constant ALREADY_PAID = 0;
/// @notice used to signal that an action should use the entire balance of a currency
/// This value is equivalent to 1<<255, i.e. a singular 1 in the most significant bit.
uint256 internal constant TOTAL_BALANCE = ActionConstants.CONTRACT_BALANCE;
/// @dev Used as a flag for identifying the transfer of ETH instead of a token
address internal constant ETH = address(0);
/// @dev The length of the bytes encoded address
uint256 internal constant ADDR_SIZE = 20;
/// @dev The length of the bytes encoded bool
uint256 internal constant BOOL_SIZE = 1;
/// @dev The minimum length of an encoding that contains 2 or more tokens
uint256 internal constant V2_MULTIPLE_TOKENS_MIN_LENGTH = ADDR_SIZE * 2;
/// @dev The length of a bytes encoded route (token0, token1, stable)
uint256 internal constant VELO_ROUTE_SIZE = ADDR_SIZE * 2 + BOOL_SIZE;
/// @dev The length of a partial bytes encoded route (useful to remove route, calculate length)
uint256 internal constant VELO_PARTIAL_ROUTE_SIZE = BOOL_SIZE + ADDR_SIZE;
/// @dev The uniswap v2 swap fee
uint256 internal constant V2_FEE = 30;
/// @dev The length of the bytes encoded fee
uint256 internal constant V3_FEE_SIZE = 3;
/// @dev The offset of a single token address (20) and pool fee (3)
uint256 internal constant NEXT_V3_POOL_OFFSET = ADDR_SIZE + V3_FEE_SIZE;
/// @dev The offset of an encoded pool key
/// Token (20) + Fee (3) + Token (20) = 43
uint256 internal constant V3_POP_OFFSET = NEXT_V3_POOL_OFFSET + ADDR_SIZE;
/// @dev The minimum length of an encoding that contains 2 or more pools
uint256 internal constant MULTIPLE_V3_POOLS_MIN_LENGTH = V3_POP_OFFSET + NEXT_V3_POOL_OFFSET;
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.24;
import {Locker} from '../libraries/Locker.sol';
/// @title Lock
/// @notice A contract that provides a reentrancy lock for external calls
contract Lock {
/// @notice Thrown when attempting to reenter a locked function from an external caller
error ContractLocked();
/// @notice Modifier enforcing a reentrancy lock that allows self-reentrancy
/// @dev If the contract is not locked, use msg.sender as the locker
modifier isNotLocked() {
// Apply a reentrancy lock for all external callers
if (msg.sender != address(this)) {
if (Locker.isLocked()) revert ContractLocked();
Locker.set(msg.sender);
_;
Locker.set(address(0));
} else {
// The contract is allowed to reenter itself, so the lock is not checked
_;
}
}
/// @notice return the current locker of the contract
function _getLocker() internal view returns (address) {
return Locker.get();
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
/// @title IWETH9
interface IWETH9 is IERC20 {
/// @notice Deposit ether to get wrapped ether
function deposit() external payable;
/// @notice Withdraw wrapped ether to get ether
function withdraw(uint256) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {ISignatureTransfer} from "./ISignatureTransfer.sol";
import {IAllowanceTransfer} from "./IAllowanceTransfer.sol";
/// @notice Permit2 handles signature-based transfers in SignatureTransfer and allowance-based transfers in AllowanceTransfer.
/// @dev Users must approve Permit2 before calling any of the transfer functions.
interface IPermit2 is ISignatureTransfer, IAllowanceTransfer {
// IPermit2 unifies the two interfaces so users have maximal flexibility with their approval.
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.24;
import {IWETH9} from '@uniswap/v4-periphery/src/interfaces/external/IWETH9.sol';
import {IPermit2} from 'permit2/src/interfaces/IPermit2.sol';
interface IPaymentImmutables {
/// @notice WETH9 address
function WETH9() external returns (IWETH9);
/// @notice Permit2 address
function PERMIT2() external returns (IPermit2);
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.24;
interface IRouterImmutables {
/// @notice The address of UniswapV2Factory
function UNISWAP_V2_FACTORY() external returns (address);
/// @notice The UniswapV2Pair initcodehash
function UNISWAP_V2_PAIR_INIT_CODE_HASH() external returns (bytes32);
/// @notice The address of UniswapV3Factory
function UNISWAP_V3_FACTORY() external returns (address);
/// @notice The UniswapV3Pool initcodehash
function UNISWAP_V3_POOL_INIT_CODE_HASH() external returns (bytes32);
/// @notice The address of Velodrome V2 PoolFactory
function VELODROME_V2_FACTORY() external returns (address);
/// @notice The VelodromeV2 Pool initcodehash
function VELODROME_V2_INIT_CODE_HASH() external returns (bytes32);
/// @notice The address of Velodrome CL PoolFactory
function VELODROME_CL_FACTORY() external returns (address);
/// @notice The Velodrome CLPool initcodehash
function VELODROME_CL_POOL_INIT_CODE_HASH() external returns (bytes32);
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.24;
import {IAllowanceTransfer} from 'permit2/src/interfaces/IAllowanceTransfer.sol';
import {SafeCast160} from 'permit2/src/libraries/SafeCast160.sol';
import {ERC20} from 'solmate/src/tokens/ERC20.sol';
import {Payments} from './Payments.sol';
/// @title Payments through Permit2
/// @notice Performs interactions with Permit2 to transfer tokens
abstract contract Permit2Payments is Payments {
using SafeCast160 for uint256;
error FromAddressIsNotOwner();
/// @notice Performs a transferFrom on Permit2
/// @param token The token to transfer
/// @param from The address to transfer from
/// @param to The recipient of the transfer
/// @param amount The amount to transfer
function permit2TransferFrom(address token, address from, address to, uint160 amount) internal {
PERMIT2.transferFrom(from, to, amount, token);
}
/// @notice Performs a batch transferFrom on Permit2
/// @param batchDetails An array detailing each of the transfers that should occur
/// @param owner The address that should be the owner of all transfers
function permit2TransferFrom(IAllowanceTransfer.AllowanceTransferDetails[] calldata batchDetails, address owner)
internal
{
uint256 batchLength = batchDetails.length;
for (uint256 i = 0; i < batchLength; ++i) {
if (batchDetails[i].from != owner) revert FromAddressIsNotOwner();
}
PERMIT2.transferFrom(batchDetails);
}
/// @notice Attempts a regular payment if payer is the router, otherwise attempts a transferFrom
/// @notice A regular transferFrom is attempted prior to Permit2
/// @param token The token to transfer
/// @param payer The address to pay for the transfer
/// @param recipient The recipient of the transfer
/// @param amount The amount to transfer
function payOrPermit2Transfer(address token, address payer, address recipient, uint256 amount) internal {
if (payer == address(this)) {
pay(token, recipient, amount);
} else {
// Try regular transferFrom before using Permit2
(bool success, bytes memory data) =
token.call(abi.encodeCall(ERC20.transferFrom, (payer, recipient, amount)));
// Fall back to Permit2 if either:
// 1. The call itself failed (reverted), or
// 2. The call succeeded but returned a non-empty response of 'false'
// (Some ERC20 tokens return false instead of reverting on failure)
if (!success || (data.length != 0 && !abi.decode(data, (bool)))) {
permit2TransferFrom(token, payer, recipient, amount.toUint160());
}
}
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.26 <0.9.0;
import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol";
import {BalanceDelta} from "@uniswap/v4-core/src/types/BalanceDelta.sol";
import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol";
import {Currency} from "@uniswap/v4-core/src/types/Currency.sol";
import {TickMath} from "@uniswap/v4-core/src/libraries/TickMath.sol";
import {SafeCast} from "@uniswap/v4-core/src/libraries/SafeCast.sol";
import {PathKey} from "./libraries/PathKey.sol";
import {CalldataDecoder} from "./libraries/CalldataDecoder.sol";
import {IV4Router} from "./interfaces/IV4Router.sol";
import {BaseActionsRouter} from "./base/BaseActionsRouter.sol";
import {DeltaResolver} from "./base/DeltaResolver.sol";
import {Actions} from "./libraries/Actions.sol";
import {ActionConstants} from "./libraries/ActionConstants.sol";
import {BipsLibrary} from "./libraries/BipsLibrary.sol";
/// @title UniswapV4Router
/// @notice Abstract contract that contains all internal logic needed for routing through Uniswap v4 pools
/// @dev the entry point to executing actions in this contract is calling `BaseActionsRouter._executeActions`
/// An inheriting contract should call _executeActions at the point that they wish actions to be executed
abstract contract V4Router is IV4Router, BaseActionsRouter, DeltaResolver {
using SafeCast for *;
using CalldataDecoder for bytes;
using BipsLibrary for uint256;
constructor(IPoolManager _poolManager) BaseActionsRouter(_poolManager) {}
function _handleAction(uint256 action, bytes calldata params) internal override {
// swap actions and payment actions in different blocks for gas efficiency
if (action < Actions.SETTLE) {
if (action == Actions.SWAP_EXACT_IN) {
IV4Router.ExactInputParams calldata swapParams = params.decodeSwapExactInParams();
_swapExactInput(swapParams);
return;
} else if (action == Actions.SWAP_EXACT_IN_SINGLE) {
IV4Router.ExactInputSingleParams calldata swapParams = params.decodeSwapExactInSingleParams();
_swapExactInputSingle(swapParams);
return;
} else if (action == Actions.SWAP_EXACT_OUT) {
IV4Router.ExactOutputParams calldata swapParams = params.decodeSwapExactOutParams();
_swapExactOutput(swapParams);
return;
} else if (action == Actions.SWAP_EXACT_OUT_SINGLE) {
IV4Router.ExactOutputSingleParams calldata swapParams = params.decodeSwapExactOutSingleParams();
_swapExactOutputSingle(swapParams);
return;
}
} else {
if (action == Actions.SETTLE_ALL) {
(Currency currency, uint256 maxAmount) = params.decodeCurrencyAndUint256();
uint256 amount = _getFullDebt(currency);
if (amount > maxAmount) revert V4TooMuchRequested(maxAmount, amount);
_settle(currency, msgSender(), amount);
return;
} else if (action == Actions.TAKE_ALL) {
(Currency currency, uint256 minAmount) = params.decodeCurrencyAndUint256();
uint256 amount = _getFullCredit(currency);
if (amount < minAmount) revert V4TooLittleReceived(minAmount, amount);
_take(currency, msgSender(), amount);
return;
} else if (action == Actions.SETTLE) {
(Currency currency, uint256 amount, bool payerIsUser) = params.decodeCurrencyUint256AndBool();
_settle(currency, _mapPayer(payerIsUser), _mapSettleAmount(amount, currency));
return;
} else if (action == Actions.TAKE) {
(Currency currency, address recipient, uint256 amount) = params.decodeCurrencyAddressAndUint256();
_take(currency, _mapRecipient(recipient), _mapTakeAmount(amount, currency));
return;
} else if (action == Actions.TAKE_PORTION) {
(Currency currency, address recipient, uint256 bips) = params.decodeCurrencyAddressAndUint256();
_take(currency, _mapRecipient(recipient), _getFullCredit(currency).calculatePortion(bips));
return;
}
}
revert UnsupportedAction(action);
}
function _swapExactInputSingle(IV4Router.ExactInputSingleParams calldata params) private {
uint128 amountIn = params.amountIn;
if (amountIn == ActionConstants.OPEN_DELTA) {
amountIn =
_getFullCredit(params.zeroForOne ? params.poolKey.currency0 : params.poolKey.currency1).toUint128();
}
uint128 amountOut =
_swap(params.poolKey, params.zeroForOne, -int256(uint256(amountIn)), params.hookData).toUint128();
if (amountOut < params.amountOutMinimum) revert V4TooLittleReceived(params.amountOutMinimum, amountOut);
}
function _swapExactInput(IV4Router.ExactInputParams calldata params) private {
unchecked {
// Caching for gas savings
uint256 pathLength = params.path.length;
uint128 amountOut;
Currency currencyIn = params.currencyIn;
uint128 amountIn = params.amountIn;
if (amountIn == ActionConstants.OPEN_DELTA) amountIn = _getFullCredit(currencyIn).toUint128();
PathKey calldata pathKey;
for (uint256 i = 0; i < pathLength; i++) {
pathKey = params.path[i];
(PoolKey memory poolKey, bool zeroForOne) = pathKey.getPoolAndSwapDirection(currencyIn);
// The output delta will always be positive, except for when interacting with certain hook pools
amountOut = _swap(poolKey, zeroForOne, -int256(uint256(amountIn)), pathKey.hookData).toUint128();
amountIn = amountOut;
currencyIn = pathKey.intermediateCurrency;
}
if (amountOut < params.amountOutMinimum) revert V4TooLittleReceived(params.amountOutMinimum, amountOut);
}
}
function _swapExactOutputSingle(IV4Router.ExactOutputSingleParams calldata params) private {
uint128 amountOut = params.amountOut;
if (amountOut == ActionConstants.OPEN_DELTA) {
amountOut =
_getFullDebt(params.zeroForOne ? params.poolKey.currency1 : params.poolKey.currency0).toUint128();
}
uint128 amountIn = (
uint256(-int256(_swap(params.poolKey, params.zeroForOne, int256(uint256(amountOut)), params.hookData)))
).toUint128();
if (amountIn > params.amountInMaximum) revert V4TooMuchRequested(params.amountInMaximum, amountIn);
}
function _swapExactOutput(IV4Router.ExactOutputParams calldata params) private {
unchecked {
// Caching for gas savings
uint256 pathLength = params.path.length;
uint128 amountIn;
uint128 amountOut = params.amountOut;
Currency currencyOut = params.currencyOut;
PathKey calldata pathKey;
if (amountOut == ActionConstants.OPEN_DELTA) {
amountOut = _getFullDebt(currencyOut).toUint128();
}
for (uint256 i = pathLength; i > 0; i--) {
pathKey = params.path[i - 1];
(PoolKey memory poolKey, bool oneForZero) = pathKey.getPoolAndSwapDirection(currencyOut);
// The output delta will always be negative, except for when interacting with certain hook pools
amountIn = (uint256(-int256(_swap(poolKey, !oneForZero, int256(uint256(amountOut)), pathKey.hookData))))
.toUint128();
amountOut = amountIn;
currencyOut = pathKey.intermediateCurrency;
}
if (amountIn > params.amountInMaximum) revert V4TooMuchRequested(params.amountInMaximum, amountIn);
}
}
function _swap(PoolKey memory poolKey, bool zeroForOne, int256 amountSpecified, bytes calldata hookData)
private
returns (int128 reciprocalAmount)
{
// for protection of exactOut swaps, sqrtPriceLimit is not exposed as a feature in this contract
unchecked {
BalanceDelta delta = poolManager.swap(
poolKey,
IPoolManager.SwapParams(
zeroForOne, amountSpecified, zeroForOne ? TickMath.MIN_SQRT_PRICE + 1 : TickMath.MAX_SQRT_PRICE - 1
),
hookData
);
reciprocalAmount = (zeroForOne == amountSpecified < 0) ? delta.amount1() : delta.amount0();
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {IERC20Minimal} from "../interfaces/external/IERC20Minimal.sol";
import {CustomRevert} from "../libraries/CustomRevert.sol";
type Currency is address;
using {greaterThan as >, lessThan as <, greaterThanOrEqualTo as >=, equals as ==} for Currency global;
using CurrencyLibrary for Currency global;
function equals(Currency currency, Currency other) pure returns (bool) {
return Currency.unwrap(currency) == Currency.unwrap(other);
}
function greaterThan(Currency currency, Currency other) pure returns (bool) {
return Currency.unwrap(currency) > Currency.unwrap(other);
}
function lessThan(Currency currency, Currency other) pure returns (bool) {
return Currency.unwrap(currency) < Currency.unwrap(other);
}
function greaterThanOrEqualTo(Currency currency, Currency other) pure returns (bool) {
return Currency.unwrap(currency) >= Currency.unwrap(other);
}
/// @title CurrencyLibrary
/// @dev This library allows for transferring and holding native tokens and ERC20 tokens
library CurrencyLibrary {
/// @notice Additional context for ERC-7751 wrapped error when a native transfer fails
error NativeTransferFailed();
/// @notice Additional context for ERC-7751 wrapped error when an ERC20 transfer fails
error ERC20TransferFailed();
/// @notice A constant to represent the native currency
Currency public constant ADDRESS_ZERO = Currency.wrap(address(0));
function transfer(Currency currency, address to, uint256 amount) internal {
// altered from https://github.com/transmissions11/solmate/blob/44a9963d4c78111f77caa0e65d677b8b46d6f2e6/src/utils/SafeTransferLib.sol
// modified custom error selectors
bool success;
if (currency.isAddressZero()) {
assembly ("memory-safe") {
// Transfer the ETH and revert if it fails.
success := call(gas(), to, amount, 0, 0, 0, 0)
}
// revert with NativeTransferFailed, containing the bubbled up error as an argument
if (!success) {
CustomRevert.bubbleUpAndRevertWith(to, bytes4(0), NativeTransferFailed.selector);
}
} else {
assembly ("memory-safe") {
// Get a pointer to some free memory.
let fmp := mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.
mstore(fmp, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
mstore(add(fmp, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
mstore(add(fmp, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.
success :=
and(
// Set success to whether the call reverted, if not we check it either
// returned exactly 1 (can't just be non-zero data), or had no return data.
or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
// We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
// Counterintuitively, this call must be positioned second to the or() call in the
// surrounding and() call or else returndatasize() will be zero during the computation.
call(gas(), currency, 0, fmp, 68, 0, 32)
)
// Now clean the memory we used
mstore(fmp, 0) // 4 byte `selector` and 28 bytes of `to` were stored here
mstore(add(fmp, 0x20), 0) // 4 bytes of `to` and 28 bytes of `amount` were stored here
mstore(add(fmp, 0x40), 0) // 4 bytes of `amount` were stored here
}
// revert with ERC20TransferFailed, containing the bubbled up error as an argument
if (!success) {
CustomRevert.bubbleUpAndRevertWith(
Currency.unwrap(currency), IERC20Minimal.transfer.selector, ERC20TransferFailed.selector
);
}
}
}
function balanceOfSelf(Currency currency) internal view returns (uint256) {
if (currency.isAddressZero()) {
return address(this).balance;
} else {
return IERC20Minimal(Currency.unwrap(currency)).balanceOf(address(this));
}
}
function balanceOf(Currency currency, address owner) internal view returns (uint256) {
if (currency.isAddressZero()) {
return owner.balance;
} else {
return IERC20Minimal(Currency.unwrap(currency)).balanceOf(owner);
}
}
function isAddressZero(Currency currency) internal pure returns (bool) {
return Currency.unwrap(currency) == Currency.unwrap(ADDRESS_ZERO);
}
function toId(Currency currency) internal pure returns (uint256) {
return uint160(Currency.unwrap(currency));
}
// If the upper 12 bytes are non-zero, they will be zero-ed out
// Therefore, fromId() and toId() are not inverses of each other
function fromId(uint256 id) internal pure returns (Currency) {
return Currency.wrap(address(uint160(id)));
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IEIP712 {
function DOMAIN_SEPARATOR() external view returns (bytes32);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol";
import {Currency} from "@uniswap/v4-core/src/types/Currency.sol";
import {PathKey} from "../libraries/PathKey.sol";
import {IImmutableState} from "./IImmutableState.sol";
/// @title IV4Router
/// @notice Interface for the V4Router contract
interface IV4Router is IImmutableState {
/// @notice Emitted when an exactInput swap does not receive its minAmountOut
error V4TooLittleReceived(uint256 minAmountOutReceived, uint256 amountReceived);
/// @notice Emitted when an exactOutput is asked for more than its maxAmountIn
error V4TooMuchRequested(uint256 maxAmountInRequested, uint256 amountRequested);
/// @notice Parameters for a single-hop exact-input swap
struct ExactInputSingleParams {
PoolKey poolKey;
bool zeroForOne;
uint128 amountIn;
uint128 amountOutMinimum;
bytes hookData;
}
/// @notice Parameters for a multi-hop exact-input swap
struct ExactInputParams {
Currency currencyIn;
PathKey[] path;
uint128 amountIn;
uint128 amountOutMinimum;
}
/// @notice Parameters for a single-hop exact-output swap
struct ExactOutputSingleParams {
PoolKey poolKey;
bool zeroForOne;
uint128 amountOut;
uint128 amountInMaximum;
bytes hookData;
}
/// @notice Parameters for a multi-hop exact-output swap
struct ExactOutputParams {
Currency currencyOut;
PathKey[] path;
uint128 amountOut;
uint128 amountInMaximum;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {PoolKey} from "../types/PoolKey.sol";
import {BalanceDelta} from "../types/BalanceDelta.sol";
import {IPoolManager} from "./IPoolManager.sol";
import {BeforeSwapDelta} from "../types/BeforeSwapDelta.sol";
/// @notice V4 decides whether to invoke specific hooks by inspecting the least significant bits
/// of the address that the hooks contract is deployed to.
/// For example, a hooks contract deployed to address: 0x0000000000000000000000000000000000002400
/// has the lowest bits '10 0100 0000 0000' which would cause the 'before initialize' and 'after add liquidity' hooks to be used.
/// See the Hooks library for the full spec.
/// @dev Should only be callable by the v4 PoolManager.
interface IHooks {
/// @notice The hook called before the state of a pool is initialized
/// @param sender The initial msg.sender for the initialize call
/// @param key The key for the pool being initialized
/// @param sqrtPriceX96 The sqrt(price) of the pool as a Q64.96
/// @return bytes4 The function selector for the hook
function beforeInitialize(address sender, PoolKey calldata key, uint160 sqrtPriceX96) external returns (bytes4);
/// @notice The hook called after the state of a pool is initialized
/// @param sender The initial msg.sender for the initialize call
/// @param key The key for the pool being initialized
/// @param sqrtPriceX96 The sqrt(price) of the pool as a Q64.96
/// @param tick The current tick after the state of a pool is initialized
/// @return bytes4 The function selector for the hook
function afterInitialize(address sender, PoolKey calldata key, uint160 sqrtPriceX96, int24 tick)
external
returns (bytes4);
/// @notice The hook called before liquidity is added
/// @param sender The initial msg.sender for the add liquidity call
/// @param key The key for the pool
/// @param params The parameters for adding liquidity
/// @param hookData Arbitrary data handed into the PoolManager by the liquidity provider to be passed on to the hook
/// @return bytes4 The function selector for the hook
function beforeAddLiquidity(
address sender,
PoolKey calldata key,
IPoolManager.ModifyLiquidityParams calldata params,
bytes calldata hookData
) external returns (bytes4);
/// @notice The hook called after liquidity is added
/// @param sender The initial msg.sender for the add liquidity call
/// @param key The key for the pool
/// @param params The parameters for adding liquidity
/// @param delta The caller's balance delta after adding liquidity; the sum of principal delta, fees accrued, and hook delta
/// @param feesAccrued The fees accrued since the last time fees were collected from this position
/// @param hookData Arbitrary data handed into the PoolManager by the liquidity provider to be passed on to the hook
/// @return bytes4 The function selector for the hook
/// @return BalanceDelta The hook's delta in token0 and token1. Positive: the hook is owed/took currency, negative: the hook owes/sent currency
function afterAddLiquidity(
address sender,
PoolKey calldata key,
IPoolManager.ModifyLiquidityParams calldata params,
BalanceDelta delta,
BalanceDelta feesAccrued,
bytes calldata hookData
) external returns (bytes4, BalanceDelta);
/// @notice The hook called before liquidity is removed
/// @param sender The initial msg.sender for the remove liquidity call
/// @param key The key for the pool
/// @param params The parameters for removing liquidity
/// @param hookData Arbitrary data handed into the PoolManager by the liquidity provider to be be passed on to the hook
/// @return bytes4 The function selector for the hook
function beforeRemoveLiquidity(
address sender,
PoolKey calldata key,
IPoolManager.ModifyLiquidityParams calldata params,
bytes calldata hookData
) external returns (bytes4);
/// @notice The hook called after liquidity is removed
/// @param sender The initial msg.sender for the remove liquidity call
/// @param key The key for the pool
/// @param params The parameters for removing liquidity
/// @param delta The caller's balance delta after removing liquidity; the sum of principal delta, fees accrued, and hook delta
/// @param feesAccrued The fees accrued since the last time fees were collected from this position
/// @param hookData Arbitrary data handed into the PoolManager by the liquidity provider to be be passed on to the hook
/// @return bytes4 The function selector for the hook
/// @return BalanceDelta The hook's delta in token0 and token1. Positive: the hook is owed/took currency, negative: the hook owes/sent currency
function afterRemoveLiquidity(
address sender,
PoolKey calldata key,
IPoolManager.ModifyLiquidityParams calldata params,
BalanceDelta delta,
BalanceDelta feesAccrued,
bytes calldata hookData
) external returns (bytes4, BalanceDelta);
/// @notice The hook called before a swap
/// @param sender The initial msg.sender for the swap call
/// @param key The key for the pool
/// @param params The parameters for the swap
/// @param hookData Arbitrary data handed into the PoolManager by the swapper to be be passed on to the hook
/// @return bytes4 The function selector for the hook
/// @return BeforeSwapDelta The hook's delta in specified and unspecified currencies. Positive: the hook is owed/took currency, negative: the hook owes/sent currency
/// @return uint24 Optionally override the lp fee, only used if three conditions are met: 1. the Pool has a dynamic fee, 2. the value's 2nd highest bit is set (23rd bit, 0x400000), and 3. the value is less than or equal to the maximum fee (1 million)
function beforeSwap(
address sender,
PoolKey calldata key,
IPoolManager.SwapParams calldata params,
bytes calldata hookData
) external returns (bytes4, BeforeSwapDelta, uint24);
/// @notice The hook called after a swap
/// @param sender The initial msg.sender for the swap call
/// @param key The key for the pool
/// @param params The parameters for the swap
/// @param delta The amount owed to the caller (positive) or owed to the pool (negative)
/// @param hookData Arbitrary data handed into the PoolManager by the swapper to be be passed on to the hook
/// @return bytes4 The function selector for the hook
/// @return int128 The hook's delta in unspecified currency. Positive: the hook is owed/took currency, negative: the hook owes/sent currency
function afterSwap(
address sender,
PoolKey calldata key,
IPoolManager.SwapParams calldata params,
BalanceDelta delta,
bytes calldata hookData
) external returns (bytes4, int128);
/// @notice The hook called before donate
/// @param sender The initial msg.sender for the donate call
/// @param key The key for the pool
/// @param amount0 The amount of token0 being donated
/// @param amount1 The amount of token1 being donated
/// @param hookData Arbitrary data handed into the PoolManager by the donor to be be passed on to the hook
/// @return bytes4 The function selector for the hook
function beforeDonate(
address sender,
PoolKey calldata key,
uint256 amount0,
uint256 amount1,
bytes calldata hookData
) external returns (bytes4);
/// @notice The hook called after donate
/// @param sender The initial msg.sender for the donate call
/// @param key The key for the pool
/// @param amount0 The amount of token0 being donated
/// @param amount1 The amount of token1 being donated
/// @param hookData Arbitrary data handed into the PoolManager by the donor to be be passed on to the hook
/// @return bytes4 The function selector for the hook
function afterDonate(
address sender,
PoolKey calldata key,
uint256 amount0,
uint256 amount1,
bytes calldata hookData
) external returns (bytes4);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {PoolKey} from "./PoolKey.sol";
type PoolId is bytes32;
/// @notice Library for computing the ID of a pool
library PoolIdLibrary {
/// @notice Returns value equal to keccak256(abi.encode(poolKey))
function toId(PoolKey memory poolKey) internal pure returns (PoolId poolId) {
assembly ("memory-safe") {
// 0xa0 represents the total size of the poolKey struct (5 slots of 32 bytes)
poolId := keccak256(poolKey, 0xa0)
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @notice Interface for claims over a contract balance, wrapped as a ERC6909
interface IERC6909Claims {
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
event OperatorSet(address indexed owner, address indexed operator, bool approved);
event Approval(address indexed owner, address indexed spender, uint256 indexed id, uint256 amount);
event Transfer(address caller, address indexed from, address indexed to, uint256 indexed id, uint256 amount);
/*//////////////////////////////////////////////////////////////
FUNCTIONS
//////////////////////////////////////////////////////////////*/
/// @notice Owner balance of an id.
/// @param owner The address of the owner.
/// @param id The id of the token.
/// @return amount The balance of the token.
function balanceOf(address owner, uint256 id) external view returns (uint256 amount);
/// @notice Spender allowance of an id.
/// @param owner The address of the owner.
/// @param spender The address of the spender.
/// @param id The id of the token.
/// @return amount The allowance of the token.
function allowance(address owner, address spender, uint256 id) external view returns (uint256 amount);
/// @notice Checks if a spender is approved by an owner as an operator
/// @param owner The address of the owner.
/// @param spender The address of the spender.
/// @return approved The approval status.
function isOperator(address owner, address spender) external view returns (bool approved);
/// @notice Transfers an amount of an id from the caller to a receiver.
/// @param receiver The address of the receiver.
/// @param id The id of the token.
/// @param amount The amount of the token.
/// @return bool True, always, unless the function reverts
function transfer(address receiver, uint256 id, uint256 amount) external returns (bool);
/// @notice Transfers an amount of an id from a sender to a receiver.
/// @param sender The address of the sender.
/// @param receiver The address of the receiver.
/// @param id The id of the token.
/// @param amount The amount of the token.
/// @return bool True, always, unless the function reverts
function transferFrom(address sender, address receiver, uint256 id, uint256 amount) external returns (bool);
/// @notice Approves an amount of an id to a spender.
/// @param spender The address of the spender.
/// @param id The id of the token.
/// @param amount The amount of the token.
/// @return bool True, always
function approve(address spender, uint256 id, uint256 amount) external returns (bool);
/// @notice Sets or removes an operator for the caller.
/// @param operator The address of the operator.
/// @param approved The approval status.
/// @return bool True, always
function setOperator(address operator, bool approved) external returns (bool);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {Currency} from "../types/Currency.sol";
import {PoolId} from "../types/PoolId.sol";
import {PoolKey} from "../types/PoolKey.sol";
/// @notice Interface for all protocol-fee related functions in the pool manager
interface IProtocolFees {
/// @notice Thrown when protocol fee is set too high
error ProtocolFeeTooLarge(uint24 fee);
/// @notice Thrown when collectProtocolFees or setProtocolFee is not called by the controller.
error InvalidCaller();
/// @notice Thrown when collectProtocolFees is attempted on a token that is synced.
error ProtocolFeeCurrencySynced();
/// @notice Emitted when the protocol fee controller address is updated in setProtocolFeeController.
event ProtocolFeeControllerUpdated(address indexed protocolFeeController);
/// @notice Emitted when the protocol fee is updated for a pool.
event ProtocolFeeUpdated(PoolId indexed id, uint24 protocolFee);
/// @notice Given a currency address, returns the protocol fees accrued in that currency
/// @param currency The currency to check
/// @return amount The amount of protocol fees accrued in the currency
function protocolFeesAccrued(Currency currency) external view returns (uint256 amount);
/// @notice Sets the protocol fee for the given pool
/// @param key The key of the pool to set a protocol fee for
/// @param newProtocolFee The fee to set
function setProtocolFee(PoolKey memory key, uint24 newProtocolFee) external;
/// @notice Sets the protocol fee controller
/// @param controller The new protocol fee controller
function setProtocolFeeController(address controller) external;
/// @notice Collects the protocol fees for a given recipient and currency, returning the amount collected
/// @dev This will revert if the contract is unlocked
/// @param recipient The address to receive the protocol fees
/// @param currency The currency to withdraw
/// @param amount The amount of currency to withdraw
/// @return amountCollected The amount of currency successfully withdrawn
function collectProtocolFees(address recipient, Currency currency, uint256 amount)
external
returns (uint256 amountCollected);
/// @notice Returns the current protocol fee controller address
/// @return address The current protocol fee controller address
function protocolFeeController() external view returns (address);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {SafeCast} from "../libraries/SafeCast.sol";
/// @dev Two `int128` values packed into a single `int256` where the upper 128 bits represent the amount0
/// and the lower 128 bits represent the amount1.
type BalanceDelta is int256;
using {add as +, sub as -, eq as ==, neq as !=} for BalanceDelta global;
using BalanceDeltaLibrary for BalanceDelta global;
using SafeCast for int256;
function toBalanceDelta(int128 _amount0, int128 _amount1) pure returns (BalanceDelta balanceDelta) {
assembly ("memory-safe") {
balanceDelta := or(shl(128, _amount0), and(sub(shl(128, 1), 1), _amount1))
}
}
function add(BalanceDelta a, BalanceDelta b) pure returns (BalanceDelta) {
int256 res0;
int256 res1;
assembly ("memory-safe") {
let a0 := sar(128, a)
let a1 := signextend(15, a)
let b0 := sar(128, b)
let b1 := signextend(15, b)
res0 := add(a0, b0)
res1 := add(a1, b1)
}
return toBalanceDelta(res0.toInt128(), res1.toInt128());
}
function sub(BalanceDelta a, BalanceDelta b) pure returns (BalanceDelta) {
int256 res0;
int256 res1;
assembly ("memory-safe") {
let a0 := sar(128, a)
let a1 := signextend(15, a)
let b0 := sar(128, b)
let b1 := signextend(15, b)
res0 := sub(a0, b0)
res1 := sub(a1, b1)
}
return toBalanceDelta(res0.toInt128(), res1.toInt128());
}
function eq(BalanceDelta a, BalanceDelta b) pure returns (bool) {
return BalanceDelta.unwrap(a) == BalanceDelta.unwrap(b);
}
function neq(BalanceDelta a, BalanceDelta b) pure returns (bool) {
return BalanceDelta.unwrap(a) != BalanceDelta.unwrap(b);
}
/// @notice Library for getting the amount0 and amount1 deltas from the BalanceDelta type
library BalanceDeltaLibrary {
/// @notice A BalanceDelta of 0
BalanceDelta public constant ZERO_DELTA = BalanceDelta.wrap(0);
function amount0(BalanceDelta balanceDelta) internal pure returns (int128 _amount0) {
assembly ("memory-safe") {
_amount0 := sar(128, balanceDelta)
}
}
function amount1(BalanceDelta balanceDelta) internal pure returns (int128 _amount1) {
assembly ("memory-safe") {
_amount1 := signextend(15, balanceDelta)
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @notice Interface for functions to access any storage slot in a contract
interface IExtsload {
/// @notice Called by external contracts to access granular pool state
/// @param slot Key of slot to sload
/// @return value The value of the slot as bytes32
function extsload(bytes32 slot) external view returns (bytes32 value);
/// @notice Called by external contracts to access granular pool state
/// @param startSlot Key of slot to start sloading from
/// @param nSlots Number of slots to load into return value
/// @return values List of loaded values.
function extsload(bytes32 startSlot, uint256 nSlots) external view returns (bytes32[] memory values);
/// @notice Called by external contracts to access sparse pool state
/// @param slots List of slots to SLOAD from.
/// @return values List of loaded values.
function extsload(bytes32[] calldata slots) external view returns (bytes32[] memory values);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
/// @notice Interface for functions to access any transient storage slot in a contract
interface IExttload {
/// @notice Called by external contracts to access transient storage of the contract
/// @param slot Key of slot to tload
/// @return value The value of the slot as bytes32
function exttload(bytes32 slot) external view returns (bytes32 value);
/// @notice Called by external contracts to access sparse transient pool state
/// @param slots List of slots to tload
/// @return values List of loaded values
function exttload(bytes32[] calldata slots) external view returns (bytes32[] memory values);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IPool {
error DepositsNotEqual();
error BelowMinimumK();
error FactoryAlreadySet();
error InsufficientLiquidity();
error InsufficientLiquidityMinted();
error InsufficientLiquidityBurned();
error InsufficientOutputAmount();
error InsufficientInputAmount();
error IsPaused();
error InvalidTo();
error K();
error NotEmergencyCouncil();
event Fees(address indexed sender, uint256 amount0, uint256 amount1);
event Mint(address indexed sender, uint256 amount0, uint256 amount1);
event Burn(address indexed sender, address indexed to, uint256 amount0, uint256 amount1);
event Swap(
address indexed sender,
address indexed to,
uint256 amount0In,
uint256 amount1In,
uint256 amount0Out,
uint256 amount1Out
);
event Sync(uint256 reserve0, uint256 reserve1);
event Claim(address indexed sender, address indexed recipient, uint256 amount0, uint256 amount1);
// Struct to capture time period obervations every 30 minutes, used for local oracles
struct Observation {
uint256 timestamp;
uint256 reserve0Cumulative;
uint256 reserve1Cumulative;
}
/// @notice Returns the decimal (dec), reserves (r), stable (st), and tokens (t) of token0 and token1
function metadata()
external
view
returns (uint256 dec0, uint256 dec1, uint256 r0, uint256 r1, bool st, address t0, address t1);
/// @notice Claim accumulated but unclaimed fees (claimable0 and claimable1)
function claimFees() external returns (uint256, uint256);
/// @notice Returns [token0, token1]
function tokens() external view returns (address, address);
/// @notice Address of token in the pool with the lower address value
function token0() external view returns (address);
/// @notice Address of token in the poool with the higher address value
function token1() external view returns (address);
/// @notice Address of linked PoolFees.sol
function poolFees() external view returns (address);
/// @notice Address of PoolFactory that created this contract
function factory() external view returns (address);
/// @notice Capture oracle reading every 30 minutes (1800 seconds)
function periodSize() external view returns (uint256);
/// @notice Amount of token0 in pool
function reserve0() external view returns (uint256);
/// @notice Amount of token1 in pool
function reserve1() external view returns (uint256);
/// @notice Timestamp of last update to pool
function blockTimestampLast() external view returns (uint256);
/// @notice Cumulative of reserve0 factoring in time elapsed
function reserve0CumulativeLast() external view returns (uint256);
/// @notice Cumulative of reserve1 factoring in time elapsed
function reserve1CumulativeLast() external view returns (uint256);
/// @notice Accumulated fees of token0 (global)
function index0() external view returns (uint256);
/// @notice Accumulated fees of token1 (global)
function index1() external view returns (uint256);
/// @notice Get an LP's relative index0 to index0
function supplyIndex0(address) external view returns (uint256);
/// @notice Get an LP's relative index1 to index1
function supplyIndex1(address) external view returns (uint256);
/// @notice Amount of unclaimed, but claimable tokens from fees of token0 for an LP
function claimable0(address) external view returns (uint256);
/// @notice Amount of unclaimed, but claimable tokens from fees of token1 for an LP
function claimable1(address) external view returns (uint256);
/// @notice Set pool name
/// Only callable by Voter.emergencyCouncil()
/// @param __name String of new name
function setName(string calldata __name) external;
/// @notice Set pool symbol
/// Only callable by Voter.emergencyCouncil()
/// @param __symbol String of new symbol
function setSymbol(string calldata __symbol) external;
/// @notice Get the number of observations recorded
function observationLength() external view returns (uint256);
/// @notice Get the value of the most recent observation
function lastObservation() external view returns (Observation memory);
/// @notice True if pool is stable, false if volatile
function stable() external view returns (bool);
/// @notice Produces the cumulative price using counterfactuals to save gas and avoid a call to sync.
function currentCumulativePrices()
external
view
returns (uint256 reserve0Cumulative, uint256 reserve1Cumulative, uint256 blockTimestamp);
/// @notice Provides twap price with user configured granularity, up to the full window size
/// @param tokenIn .
/// @param amountIn .
/// @param granularity .
/// @return amountOut .
function quote(address tokenIn, uint256 amountIn, uint256 granularity) external view returns (uint256 amountOut);
/// @notice Returns a memory set of TWAP prices
/// Same as calling sample(tokenIn, amountIn, points, 1)
/// @param tokenIn .
/// @param amountIn .
/// @param points Number of points to return
/// @return Array of TWAP prices
function prices(address tokenIn, uint256 amountIn, uint256 points) external view returns (uint256[] memory);
/// @notice Same as prices with with an additional window argument.
/// Window = 2 means 2 * 30min (or 1 hr) between observations
/// @param tokenIn .
/// @param amountIn .
/// @param points .
/// @param window .
/// @return Array of TWAP prices
function sample(address tokenIn, uint256 amountIn, uint256 points, uint256 window)
external
view
returns (uint256[] memory);
/// @notice This low-level function should be called from a contract which performs important safety checks
/// @param amount0Out Amount of token0 to send to `to`
/// @param amount1Out Amount of token1 to send to `to`
/// @param to Address to recieve the swapped output
/// @param data Additional calldata for flashloans
function swap(uint256 amount0Out, uint256 amount1Out, address to, bytes calldata data) external;
/// @notice This low-level function should be called from a contract which performs important safety checks
/// standard uniswap v2 implementation
/// @param to Address to receive token0 and token1 from burning the pool token
/// @return amount0 Amount of token0 returned
/// @return amount1 Amount of token1 returned
function burn(address to) external returns (uint256 amount0, uint256 amount1);
/// @notice This low-level function should be called by addLiquidity functions in Router.sol, which performs important safety checks
/// standard uniswap v2 implementation
/// @param to Address to receive the minted LP token
/// @return liquidity Amount of LP token minted
function mint(address to) external returns (uint256 liquidity);
/// @notice Update reserves and, on the first call per block, price accumulators
/// @return _reserve0 .
/// @return _reserve1 .
/// @return _blockTimestampLast .
function getReserves() external view returns (uint256 _reserve0, uint256 _reserve1, uint256 _blockTimestampLast);
/// @notice Get the amount of tokenOut given the amount of tokenIn
/// @param amountIn Amount of token in
/// @param tokenIn Address of token
/// @return Amount out
function getAmountOut(uint256 amountIn, address tokenIn) external view returns (uint256);
/// @notice Force balances to match reserves
/// @param to Address to receive any skimmed rewards
function skim(address to) external;
/// @notice Force reserves to match balances
function sync() external;
/// @notice Called on pool creation by PoolFactory
/// @param _token0 Address of token0
/// @param _token1 Address of token1
/// @param _stable True if stable, false if volatile
function initialize(address _token0, address _token1, bool _stable) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IPoolFactory {
event SetFeeManager(address feeManager);
event SetPauser(address pauser);
event SetPauseState(bool state);
event SetVoter(address voter);
event PoolCreated(address indexed token0, address indexed token1, bool indexed stable, address pool, uint256);
event SetCustomFee(address indexed pool, uint256 fee);
error FeeInvalid();
error FeeTooHigh();
error InvalidPool();
error NotFeeManager();
error NotPauser();
error NotSinkConverter();
error NotVoter();
error PoolAlreadyExists();
error SameAddress();
error ZeroFee();
error ZeroAddress();
/// @notice returns the number of pools created from this factory
function allPoolsLength() external view returns (uint256);
/// @notice Is a valid pool created by this factory.
/// @param .
function isPool(address pool) external view returns (bool);
/// @notice Support for Velodrome v1 which wraps around isPool(pool);
/// @param .
function isPair(address pool) external view returns (bool);
/// @notice Return address of pool created by this factory
/// @param tokenA .
/// @param tokenB .
/// @param stable True if stable, false if volatile
function getPool(address tokenA, address tokenB, bool stable) external view returns (address);
/// @notice Support for v3-style pools which wraps around getPool(tokenA,tokenB,stable)
/// @dev fee is converted to stable boolean.
/// @param tokenA .
/// @param tokenB .
/// @param fee 1 if stable, 0 if volatile, else returns address(0)
function getPool(address tokenA, address tokenB, uint24 fee) external view returns (address);
/// @notice Support for Velodrome v1 pools as a "pool" was previously referenced as "pair"
/// @notice Wraps around getPool(tokenA,tokenB,stable)
function getPair(address tokenA, address tokenB, bool stable) external view returns (address);
/// @dev Only called once to set to Voter.sol - Voter does not have a function
/// to call this contract method, so once set it's immutable.
/// This also follows convention of setVoterAndDistributor() in VotingEscrow.sol
/// @param _voter .
function setVoter(address _voter) external;
function setSinkConverter(address _sinkConvert, address _velo, address _veloV2) external;
function setPauser(address _pauser) external;
function setPauseState(bool _state) external;
function setFeeManager(address _feeManager) external;
/// @notice Set default fee for stable and volatile pools.
/// @dev Throws if higher than maximum fee.
/// Throws if fee is zero.
/// @param _stable Stable or volatile pool.
/// @param _fee .
function setFee(bool _stable, uint256 _fee) external;
/// @notice Set overriding fee for a pool from the default
/// @dev A custom fee of zero means the default fee will be used.
function setCustomFee(address _pool, uint256 _fee) external;
/// @notice Returns fee for a pool, as custom fees are possible.
function getFee(address _pool, bool _stable) external view returns (uint256);
/// @notice Create a pool given two tokens and if they're stable/volatile
/// @dev token order does not matter
/// @param tokenA .
/// @param tokenB .
/// @param stable .
function createPool(address tokenA, address tokenB, bool stable) external returns (address pool);
/// @notice Support for v3-style pools which wraps around createPool(tokena,tokenB,stable)
/// @dev fee is converted to stable boolean
/// @dev token order does not matter
/// @param tokenA .
/// @param tokenB .
/// @param fee 1 if stable, 0 if volatile, else revert
function createPool(address tokenA, address tokenB, uint24 fee) external returns (address pool);
/// @notice Support for Velodrome v1 which wraps around createPool(tokenA,tokenB,stable)
function createPair(address tokenA, address tokenB, bool stable) external returns (address pool);
function isPaused() external view returns (bool);
function velo() external view returns (address);
function veloV2() external view returns (address);
function voter() external view returns (address);
function sinkConverter() external view returns (address);
function implementation() external view returns (address);
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity >=0.8.0;
import {ERC20} from 'solmate/src/tokens/ERC20.sol';
import {IPoolFactory} from '../../../interfaces/external/IPoolFactory.sol';
import {IPool} from '../../../interfaces/external/IPool.sol';
import {Constants} from '../../../libraries/Constants.sol';
import {V2Path} from './V2Path.sol';
/// @title Uniswap v2 Helper Library
/// @notice Calculates the recipient address for a command
library UniswapV2Library {
using V2Path for bytes;
error InvalidReserves();
error StableExactOutputUnsupported();
/// @notice Calculates the v2 address for a pair
/// @param factory The address of the v2 pool factory
/// @param initCodeHash The hash of the pair initcode
/// @param salt The salt for the pair
/// @return pair The resultant v2 pair address
function pairForSalt(address factory, bytes32 initCodeHash, bytes32 salt) internal pure returns (address pair) {
pair = address(uint160(uint256(keccak256(abi.encodePacked(hex'ff', factory, salt, initCodeHash)))));
}
/// @notice Given an input asset amount returns the maximum output amount of the other asset
/// @param amountIn The token input amount
/// @param reserveIn The reserves available of the input token
/// @param reserveOut The reserves available of the output token
/// @return amountOut The output amount of the output token
function getAmountOut(uint256 amountIn, uint256 reserveIn, uint256 reserveOut)
internal
pure
returns (uint256 amountOut)
{
if (reserveIn == 0 || reserveOut == 0) revert InvalidReserves();
uint256 amountInWithFee = amountIn * 997;
uint256 numerator = amountInWithFee * reserveOut;
uint256 denominator = reserveIn * 1000 + amountInWithFee;
amountOut = numerator / denominator;
}
/// @notice Given an input asset amount returns the maximum output amount of the other asset
/// @param factory The address of the v2 pool factory
/// @param pair The address of the pair
/// @param amountIn The token input amount
/// @param reserveIn The reserves available of the input token
/// @param reserveOut The reserves available of the output token
/// @param from The address of the input token
/// @param to The address of the output token
/// @param stable Whether the pair is stable or not
/// @return amountOut The output amount of the output token
function getAmountOut(
address factory,
address pair,
uint256 amountIn,
uint256 reserveIn,
uint256 reserveOut,
address from,
address to,
bool stable
) internal view returns (uint256 amountOut) {
if (reserveIn == 0 || reserveOut == 0) revert InvalidReserves();
// adapted from _getAmountOut in Pool.sol
amountIn -= (amountIn * IPoolFactory(factory).getFee(pair, stable)) / 10_000;
if (stable) {
uint256 decimalsIn = 10 ** ERC20(from).decimals();
uint256 decimalsOut = 10 ** ERC20(to).decimals();
uint256 normalizedReserveIn = reserveIn * 1e18 / decimalsIn;
uint256 normalizedReserveOut = reserveOut * 1e18 / decimalsOut;
uint256 normalizedAmountIn = amountIn * 1e18 / decimalsIn;
uint256 xy = _f(normalizedReserveIn, normalizedReserveOut);
uint256 y = normalizedReserveOut - getY(normalizedAmountIn + normalizedReserveIn, xy, normalizedReserveOut);
return (y * decimalsOut) / 1e18;
} else {
amountOut = (amountIn * reserveOut) / (reserveIn + amountIn);
}
}
/// @notice Returns the input amount needed for a desired output amount in a single-hop trade
/// @param fee The swap fee
/// @param amountOut The desired output amount
/// @param reserveIn The reserves available of the input token
/// @param reserveOut The reserves available of the output token
/// @param stable Whether the pair is stable or not (only used for Velo, should be false for uniswap)
/// @return amountIn The input amount of the input token
function getAmountIn(uint256 fee, uint256 amountOut, uint256 reserveIn, uint256 reserveOut, bool isUni, bool stable)
internal
pure
returns (uint256 amountIn)
{
if (reserveIn == 0 || reserveOut == 0) revert InvalidReserves();
if (isUni) {
uint256 numerator = reserveIn * amountOut * 1000;
uint256 denominator = (reserveOut - amountOut) * 997;
amountIn = (numerator / denominator) + 1;
} else if (!stable) {
amountIn = (amountOut * reserveIn) / (reserveOut - amountOut);
amountIn = amountIn * 10_000 / (10_000 - fee) + 1;
} else {
revert StableExactOutputUnsupported();
}
}
/// @notice Sorts two tokens to return token0 and token1
/// @param tokenA The first token to sort
/// @param tokenB The other token to sort
/// @return token0 The smaller token by address value
/// @return token1 The larger token by address value
function sortTokens(address tokenA, address tokenB) internal pure returns (address token0, address token1) {
(token0, token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);
}
/// @notice Calculates k based on stable AMM formula given normalized reserves
/// Uses the newton raphson approximation to find a solution for the stable formula
function _f(uint256 x0, uint256 y) internal pure returns (uint256) {
uint256 _a = (x0 * y) / 1e18;
uint256 _b = ((x0 * x0) / 1e18 + (y * y) / 1e18);
return (_a * _b) / 1e18;
}
function _d(uint256 x0, uint256 y) internal pure returns (uint256) {
return (3 * x0 * ((y * y) / 1e18)) / 1e18 + ((((x0 * x0) / 1e18) * x0) / 1e18);
}
function getY(uint256 x0, uint256 xy, uint256 y) internal pure returns (uint256) {
for (uint256 i = 0; i < 255; i++) {
uint256 k = _f(x0, y);
if (k < xy) {
// there are two cases where dy == 0
// case 1: The y is converged and we find the correct answer
// case 2: _d(x0, y) is too large compare to (xy - k) and the rounding error
// screwed us.
// In this case, we need to increase y by 1
uint256 dy = ((xy - k) * 1e18) / _d(x0, y);
if (dy == 0) {
if (k == xy) {
// We found the correct answer. Return y
return y;
}
if (_f(x0, y + 1) > xy) {
// If _f(x0, y + 1) > xy, then we are close to the correct answer.
// There's no closer answer than y + 1
return y + 1;
}
dy = 1;
}
y = y + dy;
} else {
uint256 dy = ((k - xy) * 1e18) / _d(x0, y);
if (dy == 0) {
if (k == xy || _f(x0, y - 1) < xy) {
// Likewise, if k == xy, we found the correct answer.
// If _f(x0, y - 1) < xy, then we are close to the correct answer.
// There's no closer answer than "y"
// It's worth mentioning that we need to find y where f(x0, y) >= xy
// As a result, we can't return y - 1 even it's closer to the correct answer
return y;
}
dy = 1;
}
y = y - dy;
}
}
revert('!y');
}
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity >=0.6.0;
import {BytesLib} from '../v3/BytesLib.sol';
import {Constants} from '../../../libraries/Constants.sol';
/// @title Functions for manipulating path data for multihop swaps
library V2Path {
using BytesLib for bytes;
/// UniV2 functions
/// @dev Check the path has at least 2 tokens (doesnt account for incorrectly encoded arguments)
function hasMultipleTokens(bytes calldata path) internal pure returns (bool) {
return path.length >= Constants.V2_MULTIPLE_TOKENS_MIN_LENGTH;
}
/// @dev Slice the path to get last 2 tokens
function v2GetLastTokens(bytes calldata path) internal pure returns (bytes calldata) {
return path[path.length - Constants.ADDR_SIZE * 2:];
}
/// @dev Slice the path to remove the last token
function v2RemoveLastToken(bytes calldata path) internal pure returns (bytes calldata) {
return path[:path.length - Constants.ADDR_SIZE];
}
/// @dev Get the number of tokens in the path
function v2Length(bytes calldata path) internal pure returns (uint256) {
return path.length / Constants.ADDR_SIZE;
}
/// @dev Get the token0, token1 pair at the given index
function pairAt(bytes calldata path, uint256 index) internal pure returns (bytes calldata) {
uint256 start = index * Constants.ADDR_SIZE;
return path[start:start + Constants.ADDR_SIZE * 2];
}
/// @dev Get token0 and token1
function v2DecodePair(bytes calldata path) internal pure returns (address token0, address token1) {
assembly {
token0 := shr(96, calldataload(path.offset))
token1 := shr(96, calldataload(add(path.offset, 20)))
}
}
/// @dev Slice the path to get the first 2 tokens
function v2GetFirstTokens(bytes calldata path) internal pure returns (bytes calldata) {
return path[:Constants.V2_MULTIPLE_TOKENS_MIN_LENGTH];
}
/// VeloV2 functions
/// @dev Check the path has at least 1 route (doesnt account for incorrectly encoded arguments)
function hasMultipleRoutes(bytes calldata path) internal pure returns (bool) {
return path.length >= Constants.VELO_ROUTE_SIZE;
}
/// @dev Get stable param (2nd argument - between token0 and token1)
function getFirstStable(bytes calldata path) internal pure returns (bool stable) {
assembly {
stable := byte(0, calldataload(add(path.offset, 20)))
}
}
/// @dev Get token0, stable and token1 param
function decodeRoute(bytes calldata path) internal pure returns (address token0, address token1, bool stable) {
assembly {
token0 := shr(96, calldataload(path.offset))
stable := byte(0, calldataload(add(path.offset, 20)))
token1 := shr(96, calldataload(add(path.offset, 21)))
}
}
/// @dev Slice the path to remove the last route
function veloRemoveLastRoute(bytes calldata path) internal pure returns (bytes calldata) {
return path[:path.length - Constants.VELO_PARTIAL_ROUTE_SIZE];
}
/// @dev Slice the path to get the last route
function veloGetLastRoute(bytes calldata path) internal pure returns (bytes calldata) {
return path[path.length - Constants.VELO_ROUTE_SIZE:];
}
/// @dev Slice the path to get the first route
function getFirstRoute(bytes calldata path) internal pure returns (bytes calldata) {
return path[:Constants.VELO_ROUTE_SIZE];
}
/// @dev Get the number of routes in the path
/// Path is an odd number of elements. Removing the initial address makes the path divisible by (stable, address)
function veloLength(bytes calldata path) internal pure returns (uint256) {
return (path.length - Constants.ADDR_SIZE) / (Constants.VELO_PARTIAL_ROUTE_SIZE);
}
/// @dev Get the route at the given index
function veloRouteAt(bytes calldata path, uint256 index) internal pure returns (bytes calldata) {
uint256 start = index * Constants.VELO_PARTIAL_ROUTE_SIZE;
uint256 end = start + Constants.VELO_ROUTE_SIZE;
return path[start:end];
}
/// @dev Get the token0, token1 pair at the given index
function veloDecodePair(bytes calldata path) internal pure returns (address token0, address token1) {
assembly {
token0 := shr(96, calldataload(path.offset))
token1 := shr(96, calldataload(add(path.offset, 21)))
}
}
/// Common functions
/// @dev Get the first token in the path
function decodeFirstToken(bytes calldata path) internal pure returns (address tokenA) {
tokenA = path.toAddress();
}
/// @dev Get the last token in the path
function getTokenOut(bytes calldata path) internal pure returns (address tokenOut) {
assembly {
tokenOut := shr(96, calldataload(add(path.offset, sub(path.length, 20))))
}
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
/// @title Callback for IUniswapV3PoolActions#swap
/// @notice Any contract that calls IUniswapV3PoolActions#swap must implement this interface
interface IUniswapV3SwapCallback {
/// @notice Called to `msg.sender` after executing a swap via IUniswapV3Pool#swap.
/// @dev In the implementation you must pay the pool tokens owed for the swap.
/// The caller of this method must be checked to be a UniswapV3Pool deployed by the canonical UniswapV3Factory.
/// amount0Delta and amount1Delta can both be 0 if no tokens were swapped.
/// @param amount0Delta The amount of token0 that was sent (negative) or must be received (positive) by the pool by
/// the end of the swap. If positive, the callback must send that amount of token0 to the pool.
/// @param amount1Delta The amount of token1 that was sent (negative) or must be received (positive) by the pool by
/// the end of the swap. If positive, the callback must send that amount of token1 to the pool.
/// @param data Any data passed through by the caller via the IUniswapV3PoolActions#swap call
function uniswapV3SwapCallback(
int256 amount0Delta,
int256 amount1Delta,
bytes calldata data
) external;
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
/// @title Safe casting methods
/// @notice Contains methods for safely casting between types
library SafeCast {
/// @notice Cast a uint256 to a uint160, revert on overflow
/// @param y The uint256 to be downcasted
/// @return z The downcasted integer, now type uint160
function toUint160(uint256 y) internal pure returns (uint160 z) {
require((z = uint160(y)) == y);
}
/// @notice Cast a int256 to a int128, revert on overflow or underflow
/// @param y The int256 to be downcasted
/// @return z The downcasted integer, now type int128
function toInt128(int256 y) internal pure returns (int128 z) {
require((z = int128(y)) == y);
}
/// @notice Cast a uint256 to a int256, revert on overflow
/// @param y The uint256 to be casted
/// @return z The casted integer, now type int256
function toInt256(uint256 y) internal pure returns (int256 z) {
require(y < 2**255);
z = int256(y);
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
/// @title The interface for a CL Pool
/// @notice A CL pool facilitates swapping and automated market making between any two assets that strictly conform
/// to the ERC20 specification
/// @dev The pool interface is broken up into many smaller pieces
interface ICLPool {
/// @notice The first of the two tokens of the pool, sorted by address
/// @return The token contract address
function token0() external view returns (address);
/// @notice The second of the two tokens of the pool, sorted by address
/// @return The token contract address
function token1() external view returns (address);
/// @notice Swap token0 for token1, or token1 for token0
/// @dev The caller of this method receives a callback in the form of ICLSwapCallback#uniswapV3SwapCallback
/// @param recipient The address to receive the output of the swap
/// @param zeroForOne The direction of the swap, true for token0 to token1, false for token1 to token0
/// @param amountSpecified The amount of the swap, which implicitly configures the swap as exact input (positive), or exact output (negative)
/// @param sqrtPriceLimitX96 The Q64.96 sqrt price limit. If zero for one, the price cannot be less than this
/// value after the swap. If one for zero, the price cannot be greater than this value after the swap
/// @param data Any data to be passed through to the callback
/// @return amount0 The delta of the balance of token0 of the pool, exact when negative, minimum when positive
/// @return amount1 The delta of the balance of token1 of the pool, exact when negative, minimum when positive
function swap(
address recipient,
bool zeroForOne,
int256 amountSpecified,
uint160 sqrtPriceLimitX96,
bytes calldata data
) external returns (int256 amount0, int256 amount1);
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.24;
/// @notice A library used to store the maximum desired amount of input tokens for exact output swaps; used for checking slippage
library MaxInputAmount {
// The slot holding the the maximum desired amount of input tokens, transiently. bytes32(uint256(keccak256("MaxAmountIn")) - 1)
bytes32 constant MAX_AMOUNT_IN_SLOT = 0xaf28d9864a81dfdf71cab65f4e5d79a0cf9b083905fb8971425e6cb581b3f692;
function set(uint256 maxAmountIn) internal {
assembly ("memory-safe") {
tstore(MAX_AMOUNT_IN_SLOT, maxAmountIn)
}
}
function get() internal view returns (uint256 maxAmountIn) {
assembly ("memory-safe") {
maxAmountIn := tload(MAX_AMOUNT_IN_SLOT)
}
}
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity >=0.6.0;
import {BytesLib} from './BytesLib.sol';
import {Constants} from '../../../libraries/Constants.sol';
/// @title Functions for manipulating path data for multihop swaps
library V3Path {
using BytesLib for bytes;
/// @notice Returns true iff the path contains two or more pools
/// @param path The encoded swap path
/// @return True if path contains two or more pools, otherwise false
function hasMultiplePools(bytes calldata path) internal pure returns (bool) {
return path.length >= Constants.MULTIPLE_V3_POOLS_MIN_LENGTH;
}
/// @notice Decodes the first pool in path
/// @param path The bytes encoded swap path
/// @return tokenA The first token of the given pool
/// @return poolParam The parameter of the pool
/// @dev `poolParam` is `tickSpacing` in Slipstream pools and `fee` in UniV3 pools.
/// @return tokenB The second token of the given pool
function decodeFirstPool(bytes calldata path) internal pure returns (address, uint24, address) {
return path.toPool();
}
/// @notice Gets the segment corresponding to the first pool in the path
/// @param path The bytes encoded swap path
/// @return The segment containing all data necessary to target the first pool in the path
function getFirstPool(bytes calldata path) internal pure returns (bytes calldata) {
return path[:Constants.V3_POP_OFFSET];
}
function decodeFirstToken(bytes calldata path) internal pure returns (address tokenA) {
tokenA = path.toAddress();
}
/// @notice Skips a token + fee element
/// @param path The swap path
function skipToken(bytes calldata path) internal pure returns (bytes calldata) {
return path[Constants.NEXT_V3_POOL_OFFSET:];
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @title For calculating a percentage of an amount, using bips
library BipsLibrary {
uint256 internal constant BPS_DENOMINATOR = 10_000;
/// @notice emitted when an invalid percentage is provided
error InvalidBips();
/// @param amount The total amount to calculate a percentage of
/// @param bips The percentage to calculate, in bips
function calculatePortion(uint256 amount, uint256 bips) internal pure returns (uint256) {
if (bips > BPS_DENOMINATOR) revert InvalidBips();
return (amount * bips) / BPS_DENOMINATOR;
}
}// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
import {ERC20} from "../tokens/ERC20.sol";
/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
/// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer.
/// @dev Note that none of the functions in this library check that a token has code at all! That responsibility is delegated to the caller.
library SafeTransferLib {
/*//////////////////////////////////////////////////////////////
ETH OPERATIONS
//////////////////////////////////////////////////////////////*/
function safeTransferETH(address to, uint256 amount) internal {
bool success;
/// @solidity memory-safe-assembly
assembly {
// Transfer the ETH and store if it succeeded or not.
success := call(gas(), to, amount, 0, 0, 0, 0)
}
require(success, "ETH_TRANSFER_FAILED");
}
/*//////////////////////////////////////////////////////////////
ERC20 OPERATIONS
//////////////////////////////////////////////////////////////*/
function safeTransferFrom(
ERC20 token,
address from,
address to,
uint256 amount
) internal {
bool success;
/// @solidity memory-safe-assembly
assembly {
// Get a pointer to some free memory.
let freeMemoryPointer := mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.
mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), and(from, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "from" argument.
mstore(add(freeMemoryPointer, 36), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.
success := and(
// Set success to whether the call reverted, if not we check it either
// returned exactly 1 (can't just be non-zero data), or had no return data.
or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
// We use 100 because the length of our calldata totals up like so: 4 + 32 * 3.
// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
// Counterintuitively, this call must be positioned second to the or() call in the
// surrounding and() call or else returndatasize() will be zero during the computation.
call(gas(), token, 0, freeMemoryPointer, 100, 0, 32)
)
}
require(success, "TRANSFER_FROM_FAILED");
}
function safeTransfer(
ERC20 token,
address to,
uint256 amount
) internal {
bool success;
/// @solidity memory-safe-assembly
assembly {
// Get a pointer to some free memory.
let freeMemoryPointer := mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.
mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.
success := and(
// Set success to whether the call reverted, if not we check it either
// returned exactly 1 (can't just be non-zero data), or had no return data.
or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
// We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
// Counterintuitively, this call must be positioned second to the or() call in the
// surrounding and() call or else returndatasize() will be zero during the computation.
call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
)
}
require(success, "TRANSFER_FAILED");
}
function safeApprove(
ERC20 token,
address to,
uint256 amount
) internal {
bool success;
/// @solidity memory-safe-assembly
assembly {
// Get a pointer to some free memory.
let freeMemoryPointer := mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.
mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.
success := and(
// Set success to whether the call reverted, if not we check it either
// returned exactly 1 (can't just be non-zero data), or had no return data.
or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
// We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
// Counterintuitively, this call must be positioned second to the or() call in the
// surrounding and() call or else returndatasize() will be zero during the computation.
call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
)
}
require(success, "APPROVE_FAILED");
}
}// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity >=0.8.0;
import {IXERC20} from "../interfaces/IXERC20.sol";
import {HypERC20Collateral} from "../HypERC20Collateral.sol";
contract HypXERC20 is HypERC20Collateral {
constructor(
address _xerc20,
address _mailbox
) HypERC20Collateral(_xerc20, _mailbox) {
_disableInitializers();
}
function _transferFromSender(
uint256 _amountOrId
) internal override returns (bytes memory metadata) {
IXERC20(address(wrappedToken)).burn(msg.sender, _amountOrId);
return "";
}
function _transferTo(
address _recipient,
uint256 _amountOrId,
bytes calldata /*metadata*/
) internal override {
IXERC20(address(wrappedToken)).mint(_recipient, _amountOrId);
}
}// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity >=0.8.0;
/*@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@ HYPERLANE @@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@*/
/**
* Format of metadata:
*
* [0:2] variant
* [2:34] msg.value
* [34:66] Gas limit for message (IGP)
* [66:86] Refund address for message (IGP)
* [86:] Custom metadata
*/
library StandardHookMetadata {
struct Metadata {
uint16 variant;
uint256 msgValue;
uint256 gasLimit;
address refundAddress;
}
uint8 private constant VARIANT_OFFSET = 0;
uint8 private constant MSG_VALUE_OFFSET = 2;
uint8 private constant GAS_LIMIT_OFFSET = 34;
uint8 private constant REFUND_ADDRESS_OFFSET = 66;
uint256 private constant MIN_METADATA_LENGTH = 86;
uint16 public constant VARIANT = 1;
/**
* @notice Returns the variant of the metadata.
* @param _metadata ABI encoded standard hook metadata.
* @return variant of the metadata as uint8.
*/
function variant(bytes calldata _metadata) internal pure returns (uint16) {
if (_metadata.length < VARIANT_OFFSET + 2) return 0;
return uint16(bytes2(_metadata[VARIANT_OFFSET:VARIANT_OFFSET + 2]));
}
/**
* @notice Returns the specified value for the message.
* @param _metadata ABI encoded standard hook metadata.
* @param _default Default fallback value.
* @return Value for the message as uint256.
*/
function msgValue(
bytes calldata _metadata,
uint256 _default
) internal pure returns (uint256) {
if (_metadata.length < MSG_VALUE_OFFSET + 32) return _default;
return
uint256(bytes32(_metadata[MSG_VALUE_OFFSET:MSG_VALUE_OFFSET + 32]));
}
/**
* @notice Returns the specified gas limit for the message.
* @param _metadata ABI encoded standard hook metadata.
* @param _default Default fallback gas limit.
* @return Gas limit for the message as uint256.
*/
function gasLimit(
bytes calldata _metadata,
uint256 _default
) internal pure returns (uint256) {
if (_metadata.length < GAS_LIMIT_OFFSET + 32) return _default;
return
uint256(bytes32(_metadata[GAS_LIMIT_OFFSET:GAS_LIMIT_OFFSET + 32]));
}
/**
* @notice Returns the specified refund address for the message.
* @param _metadata ABI encoded standard hook metadata.
* @param _default Default fallback refund address.
* @return Refund address for the message as address.
*/
function refundAddress(
bytes calldata _metadata,
address _default
) internal pure returns (address) {
if (_metadata.length < REFUND_ADDRESS_OFFSET + 20) return _default;
return
address(
bytes20(
_metadata[REFUND_ADDRESS_OFFSET:REFUND_ADDRESS_OFFSET + 20]
)
);
}
/**
* @notice Returns any custom metadata.
* @param _metadata ABI encoded standard hook metadata.
* @return Custom metadata.
*/
function getCustomMetadata(
bytes calldata _metadata
) internal pure returns (bytes calldata) {
if (_metadata.length < MIN_METADATA_LENGTH) return _metadata[0:0];
return _metadata[MIN_METADATA_LENGTH:];
}
/**
* @notice Formats the specified gas limit and refund address into standard hook metadata.
* @param _msgValue msg.value for the message.
* @param _gasLimit Gas limit for the message.
* @param _refundAddress Refund address for the message.
* @param _customMetadata Additional metadata to include in the standard hook metadata.
* @return ABI encoded standard hook metadata.
*/
function formatMetadata(
uint256 _msgValue,
uint256 _gasLimit,
address _refundAddress,
bytes memory _customMetadata
) internal pure returns (bytes memory) {
return
abi.encodePacked(
VARIANT,
_msgValue,
_gasLimit,
_refundAddress,
_customMetadata
);
}
/**
* @notice Formats the specified gas limit and refund address into standard hook metadata.
* @param _msgValue msg.value for the message.
* @return ABI encoded standard hook metadata.
*/
function overrideMsgValue(
uint256 _msgValue
) internal view returns (bytes memory) {
return formatMetadata(_msgValue, uint256(0), msg.sender, "");
}
/**
* @notice Formats the specified gas limit and refund address into standard hook metadata.
* @param _gasLimit Gas limit for the message.
* @return ABI encoded standard hook metadata.
*/
function overrideGasLimit(
uint256 _gasLimit
) internal view returns (bytes memory) {
return formatMetadata(uint256(0), _gasLimit, msg.sender, "");
}
/**
* @notice Formats the specified refund address into standard hook metadata.
* @param _refundAddress Refund address for the message.
* @return ABI encoded standard hook metadata.
*/
function overrideRefundAddress(
address _refundAddress
) internal pure returns (bytes memory) {
return formatMetadata(uint256(0), uint256(0), _refundAddress, "");
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {IInterchainSecurityModule} from '@hyperlane/core/contracts/interfaces/IInterchainSecurityModule.sol';
interface ITokenBridge {
error NotBridge();
error ZeroAmount();
error ZeroAddress();
error InsufficientBalance();
event HookSet(address indexed _newHook);
event SentMessage(
uint32 indexed _destination, bytes32 indexed _recipient, uint256 _value, string _message, string _metadata
);
/// @notice Max gas limit for token bridging transactions
/// @dev Can set a different gas limit by using a custom hook
function GAS_LIMIT() external view returns (uint256);
/// @notice Returns the address of the xERC20 token that is bridged by this contract
function xerc20() external view returns (address);
/// @notice The underlying ERC20 token of the lockbox
function erc20() external view returns (address);
/// @notice Returns the address of the mailbox contract that is used to bridge by this contract
function mailbox() external view returns (address);
/// @notice Returns the address of the hook contract used after dispatching a message
/// @dev If set to zero address, default hook will be used instead
function hook() external view returns (address);
/// @notice Returns the address of the security module contract used by the bridge
function securityModule() external view returns (IInterchainSecurityModule);
/// @notice Sets the address of the hook contract that will be used in bridging
/// @dev Can use default hook by setting to zero address
/// @param _hook The address of the new hook contract
function setHook(address _hook) external;
/// @notice Burns xERC20 tokens from the sender and triggers a x-chain transfer
/// @dev If bridging from/to Root, ERC20 tokens are wrapped into xERC20 for bridging and unwrapped back when received.
/// @param _recipient The address of the recipient on the destination chain
/// @param _amount The amount of xERC20 tokens to send
/// @param _domain The domain of the destination chain
function sendToken(address _recipient, uint256 _amount, uint32 _domain) external payable;
/// @notice Burns xERC20 tokens from the sender and triggers a x-chain transfer
/// @dev If bridging from/to Root, ERC20 tokens are wrapped into xERC20 for bridging and unwrapped back when received.
/// @dev Refunds go to the specified _refundAddress
/// @param _recipient The address of the recipient on the destination chain
/// @param _amount The amount of xERC20 tokens to send
/// @param _domain The domain of the destination chain
/// @param _refundAddress The address to send the excess eth to
function sendToken(address _recipient, uint256 _amount, uint32 _domain, address _refundAddress) external payable;
/// @notice Registers a domain on the bridge
/// @param _domain The domain to register
function registerDomain(uint32 _domain) external;
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.24;
/// @title BridgeTypes
/// @notice Defines bridge types
library BridgeTypes {
// Bridge type identifiers (1 byte)
uint8 constant HYP_XERC20 = 0x01;
uint8 constant XVELO = 0x02;
// Future bridge types can be added here
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.24;
/// @notice A library to implement a reentrancy lock in transient storage.
/// @dev Instead of storing a boolean, the locker's address is stored to allow the contract to know who locked the contract
/// TODO: This library can be deleted when we have the transient keyword support in solidity.
library Locker {
// The slot holding the locker state, transiently. bytes32(uint256(keccak256("Locker")) - 1)
bytes32 constant LOCKER_SLOT = 0x0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a708;
function set(address locker) internal {
// The locker is always msg.sender or address(0) so does not need to be cleaned
assembly ("memory-safe") {
tstore(LOCKER_SLOT, locker)
}
}
function get() internal view returns (address locker) {
assembly ("memory-safe") {
locker := tload(LOCKER_SLOT)
}
}
function isLocked() internal view returns (bool) {
return Locker.get() != address(0);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @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);
/**
* @dev Returns the value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of 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 value) 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 a `value` amount of tokens 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 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` 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 value) external returns (bool);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {IEIP712} from "./IEIP712.sol";
/// @title SignatureTransfer
/// @notice Handles ERC20 token transfers through signature based actions
/// @dev Requires user's token approval on the Permit2 contract
interface ISignatureTransfer is IEIP712 {
/// @notice Thrown when the requested amount for a transfer is larger than the permissioned amount
/// @param maxAmount The maximum amount a spender can request to transfer
error InvalidAmount(uint256 maxAmount);
/// @notice Thrown when the number of tokens permissioned to a spender does not match the number of tokens being transferred
/// @dev If the spender does not need to transfer the number of tokens permitted, the spender can request amount 0 to be transferred
error LengthMismatch();
/// @notice Emits an event when the owner successfully invalidates an unordered nonce.
event UnorderedNonceInvalidation(address indexed owner, uint256 word, uint256 mask);
/// @notice The token and amount details for a transfer signed in the permit transfer signature
struct TokenPermissions {
// ERC20 token address
address token;
// the maximum amount that can be spent
uint256 amount;
}
/// @notice The signed permit message for a single token transfer
struct PermitTransferFrom {
TokenPermissions permitted;
// a unique value for every token owner's signature to prevent signature replays
uint256 nonce;
// deadline on the permit signature
uint256 deadline;
}
/// @notice Specifies the recipient address and amount for batched transfers.
/// @dev Recipients and amounts correspond to the index of the signed token permissions array.
/// @dev Reverts if the requested amount is greater than the permitted signed amount.
struct SignatureTransferDetails {
// recipient address
address to;
// spender requested amount
uint256 requestedAmount;
}
/// @notice Used to reconstruct the signed permit message for multiple token transfers
/// @dev Do not need to pass in spender address as it is required that it is msg.sender
/// @dev Note that a user still signs over a spender address
struct PermitBatchTransferFrom {
// the tokens and corresponding amounts permitted for a transfer
TokenPermissions[] permitted;
// a unique value for every token owner's signature to prevent signature replays
uint256 nonce;
// deadline on the permit signature
uint256 deadline;
}
/// @notice A map from token owner address and a caller specified word index to a bitmap. Used to set bits in the bitmap to prevent against signature replay protection
/// @dev Uses unordered nonces so that permit messages do not need to be spent in a certain order
/// @dev The mapping is indexed first by the token owner, then by an index specified in the nonce
/// @dev It returns a uint256 bitmap
/// @dev The index, or wordPosition is capped at type(uint248).max
function nonceBitmap(address, uint256) external view returns (uint256);
/// @notice Transfers a token using a signed permit message
/// @dev Reverts if the requested amount is greater than the permitted signed amount
/// @param permit The permit data signed over by the owner
/// @param owner The owner of the tokens to transfer
/// @param transferDetails The spender's requested transfer details for the permitted token
/// @param signature The signature to verify
function permitTransferFrom(
PermitTransferFrom memory permit,
SignatureTransferDetails calldata transferDetails,
address owner,
bytes calldata signature
) external;
/// @notice Transfers a token using a signed permit message
/// @notice Includes extra data provided by the caller to verify signature over
/// @dev The witness type string must follow EIP712 ordering of nested structs and must include the TokenPermissions type definition
/// @dev Reverts if the requested amount is greater than the permitted signed amount
/// @param permit The permit data signed over by the owner
/// @param owner The owner of the tokens to transfer
/// @param transferDetails The spender's requested transfer details for the permitted token
/// @param witness Extra data to include when checking the user signature
/// @param witnessTypeString The EIP-712 type definition for remaining string stub of the typehash
/// @param signature The signature to verify
function permitWitnessTransferFrom(
PermitTransferFrom memory permit,
SignatureTransferDetails calldata transferDetails,
address owner,
bytes32 witness,
string calldata witnessTypeString,
bytes calldata signature
) external;
/// @notice Transfers multiple tokens using a signed permit message
/// @param permit The permit data signed over by the owner
/// @param owner The owner of the tokens to transfer
/// @param transferDetails Specifies the recipient and requested amount for the token transfer
/// @param signature The signature to verify
function permitTransferFrom(
PermitBatchTransferFrom memory permit,
SignatureTransferDetails[] calldata transferDetails,
address owner,
bytes calldata signature
) external;
/// @notice Transfers multiple tokens using a signed permit message
/// @dev The witness type string must follow EIP712 ordering of nested structs and must include the TokenPermissions type definition
/// @notice Includes extra data provided by the caller to verify signature over
/// @param permit The permit data signed over by the owner
/// @param owner The owner of the tokens to transfer
/// @param transferDetails Specifies the recipient and requested amount for the token transfer
/// @param witness Extra data to include when checking the user signature
/// @param witnessTypeString The EIP-712 type definition for remaining string stub of the typehash
/// @param signature The signature to verify
function permitWitnessTransferFrom(
PermitBatchTransferFrom memory permit,
SignatureTransferDetails[] calldata transferDetails,
address owner,
bytes32 witness,
string calldata witnessTypeString,
bytes calldata signature
) external;
/// @notice Invalidates the bits specified in mask for the bitmap at the word position
/// @dev The wordPos is maxed at type(uint248).max
/// @param wordPos A number to index the nonceBitmap at
/// @param mask A bitmap masked against msg.sender's current bitmap at the word position
function invalidateUnorderedNonces(uint256 wordPos, uint256 mask) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
library SafeCast160 {
/// @notice Thrown when a valude greater than type(uint160).max is cast to uint160
error UnsafeCast();
/// @notice Safely casts uint256 to uint160
/// @param value The uint256 to be cast
function toUint160(uint256 value) internal pure returns (uint160) {
if (value > type(uint160).max) revert UnsafeCast();
return uint160(value);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {BitMath} from "./BitMath.sol";
import {CustomRevert} from "./CustomRevert.sol";
/// @title Math library for computing sqrt prices from ticks and vice versa
/// @notice Computes sqrt price for ticks of size 1.0001, i.e. sqrt(1.0001^tick) as fixed point Q64.96 numbers. Supports
/// prices between 2**-128 and 2**128
library TickMath {
using CustomRevert for bytes4;
/// @notice Thrown when the tick passed to #getSqrtPriceAtTick is not between MIN_TICK and MAX_TICK
error InvalidTick(int24 tick);
/// @notice Thrown when the price passed to #getTickAtSqrtPrice does not correspond to a price between MIN_TICK and MAX_TICK
error InvalidSqrtPrice(uint160 sqrtPriceX96);
/// @dev The minimum tick that may be passed to #getSqrtPriceAtTick computed from log base 1.0001 of 2**-128
/// @dev If ever MIN_TICK and MAX_TICK are not centered around 0, the absTick logic in getSqrtPriceAtTick cannot be used
int24 internal constant MIN_TICK = -887272;
/// @dev The maximum tick that may be passed to #getSqrtPriceAtTick computed from log base 1.0001 of 2**128
/// @dev If ever MIN_TICK and MAX_TICK are not centered around 0, the absTick logic in getSqrtPriceAtTick cannot be used
int24 internal constant MAX_TICK = 887272;
/// @dev The minimum tick spacing value drawn from the range of type int16 that is greater than 0, i.e. min from the range [1, 32767]
int24 internal constant MIN_TICK_SPACING = 1;
/// @dev The maximum tick spacing value drawn from the range of type int16, i.e. max from the range [1, 32767]
int24 internal constant MAX_TICK_SPACING = type(int16).max;
/// @dev The minimum value that can be returned from #getSqrtPriceAtTick. Equivalent to getSqrtPriceAtTick(MIN_TICK)
uint160 internal constant MIN_SQRT_PRICE = 4295128739;
/// @dev The maximum value that can be returned from #getSqrtPriceAtTick. Equivalent to getSqrtPriceAtTick(MAX_TICK)
uint160 internal constant MAX_SQRT_PRICE = 1461446703485210103287273052203988822378723970342;
/// @dev A threshold used for optimized bounds check, equals `MAX_SQRT_PRICE - MIN_SQRT_PRICE - 1`
uint160 internal constant MAX_SQRT_PRICE_MINUS_MIN_SQRT_PRICE_MINUS_ONE =
1461446703485210103287273052203988822378723970342 - 4295128739 - 1;
/// @notice Given a tickSpacing, compute the maximum usable tick
function maxUsableTick(int24 tickSpacing) internal pure returns (int24) {
unchecked {
return (MAX_TICK / tickSpacing) * tickSpacing;
}
}
/// @notice Given a tickSpacing, compute the minimum usable tick
function minUsableTick(int24 tickSpacing) internal pure returns (int24) {
unchecked {
return (MIN_TICK / tickSpacing) * tickSpacing;
}
}
/// @notice Calculates sqrt(1.0001^tick) * 2^96
/// @dev Throws if |tick| > max tick
/// @param tick The input tick for the above formula
/// @return sqrtPriceX96 A Fixed point Q64.96 number representing the sqrt of the price of the two assets (currency1/currency0)
/// at the given tick
function getSqrtPriceAtTick(int24 tick) internal pure returns (uint160 sqrtPriceX96) {
unchecked {
uint256 absTick;
assembly ("memory-safe") {
tick := signextend(2, tick)
// mask = 0 if tick >= 0 else -1 (all 1s)
let mask := sar(255, tick)
// if tick >= 0, |tick| = tick = 0 ^ tick
// if tick < 0, |tick| = ~~|tick| = ~(-|tick| - 1) = ~(tick - 1) = (-1) ^ (tick - 1)
// either way, |tick| = mask ^ (tick + mask)
absTick := xor(mask, add(mask, tick))
}
if (absTick > uint256(int256(MAX_TICK))) InvalidTick.selector.revertWith(tick);
// The tick is decomposed into bits, and for each bit with index i that is set, the product of 1/sqrt(1.0001^(2^i))
// is calculated (using Q128.128). The constants used for this calculation are rounded to the nearest integer
// Equivalent to:
// price = absTick & 0x1 != 0 ? 0xfffcb933bd6fad37aa2d162d1a594001 : 0x100000000000000000000000000000000;
// or price = int(2**128 / sqrt(1.0001)) if (absTick & 0x1) else 1 << 128
uint256 price;
assembly ("memory-safe") {
price := xor(shl(128, 1), mul(xor(shl(128, 1), 0xfffcb933bd6fad37aa2d162d1a594001), and(absTick, 0x1)))
}
if (absTick & 0x2 != 0) price = (price * 0xfff97272373d413259a46990580e213a) >> 128;
if (absTick & 0x4 != 0) price = (price * 0xfff2e50f5f656932ef12357cf3c7fdcc) >> 128;
if (absTick & 0x8 != 0) price = (price * 0xffe5caca7e10e4e61c3624eaa0941cd0) >> 128;
if (absTick & 0x10 != 0) price = (price * 0xffcb9843d60f6159c9db58835c926644) >> 128;
if (absTick & 0x20 != 0) price = (price * 0xff973b41fa98c081472e6896dfb254c0) >> 128;
if (absTick & 0x40 != 0) price = (price * 0xff2ea16466c96a3843ec78b326b52861) >> 128;
if (absTick & 0x80 != 0) price = (price * 0xfe5dee046a99a2a811c461f1969c3053) >> 128;
if (absTick & 0x100 != 0) price = (price * 0xfcbe86c7900a88aedcffc83b479aa3a4) >> 128;
if (absTick & 0x200 != 0) price = (price * 0xf987a7253ac413176f2b074cf7815e54) >> 128;
if (absTick & 0x400 != 0) price = (price * 0xf3392b0822b70005940c7a398e4b70f3) >> 128;
if (absTick & 0x800 != 0) price = (price * 0xe7159475a2c29b7443b29c7fa6e889d9) >> 128;
if (absTick & 0x1000 != 0) price = (price * 0xd097f3bdfd2022b8845ad8f792aa5825) >> 128;
if (absTick & 0x2000 != 0) price = (price * 0xa9f746462d870fdf8a65dc1f90e061e5) >> 128;
if (absTick & 0x4000 != 0) price = (price * 0x70d869a156d2a1b890bb3df62baf32f7) >> 128;
if (absTick & 0x8000 != 0) price = (price * 0x31be135f97d08fd981231505542fcfa6) >> 128;
if (absTick & 0x10000 != 0) price = (price * 0x9aa508b5b7a84e1c677de54f3e99bc9) >> 128;
if (absTick & 0x20000 != 0) price = (price * 0x5d6af8dedb81196699c329225ee604) >> 128;
if (absTick & 0x40000 != 0) price = (price * 0x2216e584f5fa1ea926041bedfe98) >> 128;
if (absTick & 0x80000 != 0) price = (price * 0x48a170391f7dc42444e8fa2) >> 128;
assembly ("memory-safe") {
// if (tick > 0) price = type(uint256).max / price;
if sgt(tick, 0) { price := div(not(0), price) }
// this divides by 1<<32 rounding up to go from a Q128.128 to a Q128.96.
// we then downcast because we know the result always fits within 160 bits due to our tick input constraint
// we round up in the division so getTickAtSqrtPrice of the output price is always consistent
// `sub(shl(32, 1), 1)` is `type(uint32).max`
// `price + type(uint32).max` will not overflow because `price` fits in 192 bits
sqrtPriceX96 := shr(32, add(price, sub(shl(32, 1), 1)))
}
}
}
/// @notice Calculates the greatest tick value such that getSqrtPriceAtTick(tick) <= sqrtPriceX96
/// @dev Throws in case sqrtPriceX96 < MIN_SQRT_PRICE, as MIN_SQRT_PRICE is the lowest value getSqrtPriceAtTick may
/// ever return.
/// @param sqrtPriceX96 The sqrt price for which to compute the tick as a Q64.96
/// @return tick The greatest tick for which the getSqrtPriceAtTick(tick) is less than or equal to the input sqrtPriceX96
function getTickAtSqrtPrice(uint160 sqrtPriceX96) internal pure returns (int24 tick) {
unchecked {
// Equivalent: if (sqrtPriceX96 < MIN_SQRT_PRICE || sqrtPriceX96 >= MAX_SQRT_PRICE) revert InvalidSqrtPrice();
// second inequality must be >= because the price can never reach the price at the max tick
// if sqrtPriceX96 < MIN_SQRT_PRICE, the `sub` underflows and `gt` is true
// if sqrtPriceX96 >= MAX_SQRT_PRICE, sqrtPriceX96 - MIN_SQRT_PRICE > MAX_SQRT_PRICE - MIN_SQRT_PRICE - 1
if ((sqrtPriceX96 - MIN_SQRT_PRICE) > MAX_SQRT_PRICE_MINUS_MIN_SQRT_PRICE_MINUS_ONE) {
InvalidSqrtPrice.selector.revertWith(sqrtPriceX96);
}
uint256 price = uint256(sqrtPriceX96) << 32;
uint256 r = price;
uint256 msb = BitMath.mostSignificantBit(r);
if (msb >= 128) r = price >> (msb - 127);
else r = price << (127 - msb);
int256 log_2 = (int256(msb) - 128) << 64;
assembly ("memory-safe") {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(63, f))
r := shr(f, r)
}
assembly ("memory-safe") {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(62, f))
r := shr(f, r)
}
assembly ("memory-safe") {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(61, f))
r := shr(f, r)
}
assembly ("memory-safe") {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(60, f))
r := shr(f, r)
}
assembly ("memory-safe") {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(59, f))
r := shr(f, r)
}
assembly ("memory-safe") {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(58, f))
r := shr(f, r)
}
assembly ("memory-safe") {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(57, f))
r := shr(f, r)
}
assembly ("memory-safe") {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(56, f))
r := shr(f, r)
}
assembly ("memory-safe") {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(55, f))
r := shr(f, r)
}
assembly ("memory-safe") {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(54, f))
r := shr(f, r)
}
assembly ("memory-safe") {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(53, f))
r := shr(f, r)
}
assembly ("memory-safe") {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(52, f))
r := shr(f, r)
}
assembly ("memory-safe") {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(51, f))
r := shr(f, r)
}
assembly ("memory-safe") {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(50, f))
}
int256 log_sqrt10001 = log_2 * 255738958999603826347141; // Q22.128 number
// Magic number represents the ceiling of the maximum value of the error when approximating log_sqrt10001(x)
int24 tickLow = int24((log_sqrt10001 - 3402992956809132418596140100660247210) >> 128);
// Magic number represents the minimum value of the error when approximating log_sqrt10001(x), when
// sqrtPrice is from the range (2^-64, 2^64). This is safe as MIN_SQRT_PRICE is more than 2^-64. If MIN_SQRT_PRICE
// is changed, this may need to be changed too
int24 tickHi = int24((log_sqrt10001 + 291339464771989622907027621153398088495) >> 128);
tick = tickLow == tickHi ? tickLow : getSqrtPriceAtTick(tickHi) <= sqrtPriceX96 ? tickHi : tickLow;
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {CustomRevert} from "./CustomRevert.sol";
/// @title Safe casting methods
/// @notice Contains methods for safely casting between types
library SafeCast {
using CustomRevert for bytes4;
error SafeCastOverflow();
/// @notice Cast a uint256 to a uint160, revert on overflow
/// @param x The uint256 to be downcasted
/// @return y The downcasted integer, now type uint160
function toUint160(uint256 x) internal pure returns (uint160 y) {
y = uint160(x);
if (y != x) SafeCastOverflow.selector.revertWith();
}
/// @notice Cast a uint256 to a uint128, revert on overflow
/// @param x The uint256 to be downcasted
/// @return y The downcasted integer, now type uint128
function toUint128(uint256 x) internal pure returns (uint128 y) {
y = uint128(x);
if (x != y) SafeCastOverflow.selector.revertWith();
}
/// @notice Cast a int128 to a uint128, revert on overflow or underflow
/// @param x The int128 to be casted
/// @return y The casted integer, now type uint128
function toUint128(int128 x) internal pure returns (uint128 y) {
if (x < 0) SafeCastOverflow.selector.revertWith();
y = uint128(x);
}
/// @notice Cast a int256 to a int128, revert on overflow or underflow
/// @param x The int256 to be downcasted
/// @return y The downcasted integer, now type int128
function toInt128(int256 x) internal pure returns (int128 y) {
y = int128(x);
if (y != x) SafeCastOverflow.selector.revertWith();
}
/// @notice Cast a uint256 to a int256, revert on overflow
/// @param x The uint256 to be casted
/// @return y The casted integer, now type int256
function toInt256(uint256 x) internal pure returns (int256 y) {
y = int256(x);
if (y < 0) SafeCastOverflow.selector.revertWith();
}
/// @notice Cast a uint256 to a int128, revert on overflow
/// @param x The uint256 to be downcasted
/// @return The downcasted integer, now type int128
function toInt128(uint256 x) internal pure returns (int128) {
if (x >= 1 << 127) SafeCastOverflow.selector.revertWith();
return int128(int256(x));
}
}//SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {Currency} from "@uniswap/v4-core/src/types/Currency.sol";
import {IHooks} from "@uniswap/v4-core/src/interfaces/IHooks.sol";
import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol";
struct PathKey {
Currency intermediateCurrency;
uint24 fee;
int24 tickSpacing;
IHooks hooks;
bytes hookData;
}
using PathKeyLibrary for PathKey global;
/// @title PathKey Library
/// @notice Functions for working with PathKeys
library PathKeyLibrary {
/// @notice Get the pool and swap direction for a given PathKey
/// @param params the given PathKey
/// @param currencyIn the input currency
/// @return poolKey the pool key of the swap
/// @return zeroForOne the direction of the swap, true if currency0 is being swapped for currency1
function getPoolAndSwapDirection(PathKey calldata params, Currency currencyIn)
internal
pure
returns (PoolKey memory poolKey, bool zeroForOne)
{
Currency currencyOut = params.intermediateCurrency;
(Currency currency0, Currency currency1) =
currencyIn < currencyOut ? (currencyIn, currencyOut) : (currencyOut, currencyIn);
zeroForOne = currencyIn == currency0;
poolKey = PoolKey(currency0, currency1, params.fee, params.tickSpacing, params.hooks);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol";
import {SafeCallback} from "./SafeCallback.sol";
import {CalldataDecoder} from "../libraries/CalldataDecoder.sol";
import {ActionConstants} from "../libraries/ActionConstants.sol";
/// @notice Abstract contract for performing a combination of actions on Uniswap v4.
/// @dev Suggested uint256 action values are defined in Actions.sol, however any definition can be used
abstract contract BaseActionsRouter is SafeCallback {
using CalldataDecoder for bytes;
/// @notice emitted when different numbers of parameters and actions are provided
error InputLengthMismatch();
/// @notice emitted when an inheriting contract does not support an action
error UnsupportedAction(uint256 action);
constructor(IPoolManager _poolManager) SafeCallback(_poolManager) {}
/// @notice internal function that triggers the execution of a set of actions on v4
/// @dev inheriting contracts should call this function to trigger execution
function _executeActions(bytes calldata unlockData) internal {
poolManager.unlock(unlockData);
}
/// @notice function that is called by the PoolManager through the SafeCallback.unlockCallback
/// @param data Abi encoding of (bytes actions, bytes[] params)
/// where params[i] is the encoded parameters for actions[i]
function _unlockCallback(bytes calldata data) internal override returns (bytes memory) {
// abi.decode(data, (bytes, bytes[]));
(bytes calldata actions, bytes[] calldata params) = data.decodeActionsRouterParams();
_executeActionsWithoutUnlock(actions, params);
return "";
}
function _executeActionsWithoutUnlock(bytes calldata actions, bytes[] calldata params) internal {
uint256 numActions = actions.length;
if (numActions != params.length) revert InputLengthMismatch();
for (uint256 actionIndex = 0; actionIndex < numActions; actionIndex++) {
uint256 action = uint8(actions[actionIndex]);
_handleAction(action, params[actionIndex]);
}
}
/// @notice function to handle the parsing and execution of an action and its parameters
function _handleAction(uint256 action, bytes calldata params) internal virtual;
/// @notice function that returns address considered executor of the actions
/// @dev The other context functions, _msgData and _msgValue, are not supported by this contract
/// In many contracts this will be the address that calls the initial entry point that calls `_executeActions`
/// `msg.sender` shouldn't be used, as this will be the v4 pool manager contract that calls `unlockCallback`
/// If using ReentrancyLock.sol, this function can return _getLocker()
function msgSender() public view virtual returns (address);
/// @notice Calculates the address for a action
function _mapRecipient(address recipient) internal view returns (address) {
if (recipient == ActionConstants.MSG_SENDER) {
return msgSender();
} else if (recipient == ActionConstants.ADDRESS_THIS) {
return address(this);
} else {
return recipient;
}
}
/// @notice Calculates the payer for an action
function _mapPayer(bool payerIsUser) internal view returns (address) {
return payerIsUser ? msgSender() : address(this);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import {Currency} from "@uniswap/v4-core/src/types/Currency.sol";
import {TransientStateLibrary} from "@uniswap/v4-core/src/libraries/TransientStateLibrary.sol";
import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol";
import {ImmutableState} from "./ImmutableState.sol";
import {ActionConstants} from "../libraries/ActionConstants.sol";
/// @notice Abstract contract used to sync, send, and settle funds to the pool manager
/// @dev Note that sync() is called before any erc-20 transfer in `settle`.
abstract contract DeltaResolver is ImmutableState {
using TransientStateLibrary for IPoolManager;
/// @notice Emitted trying to settle a positive delta.
error DeltaNotPositive(Currency currency);
/// @notice Emitted trying to take a negative delta.
error DeltaNotNegative(Currency currency);
/// @notice Emitted when the contract does not have enough balance to wrap or unwrap.
error InsufficientBalance();
/// @notice Take an amount of currency out of the PoolManager
/// @param currency Currency to take
/// @param recipient Address to receive the currency
/// @param amount Amount to take
/// @dev Returns early if the amount is 0
function _take(Currency currency, address recipient, uint256 amount) internal {
if (amount == 0) return;
poolManager.take(currency, recipient, amount);
}
/// @notice Pay and settle a currency to the PoolManager
/// @dev The implementing contract must ensure that the `payer` is a secure address
/// @param currency Currency to settle
/// @param payer Address of the payer
/// @param amount Amount to send
/// @dev Returns early if the amount is 0
function _settle(Currency currency, address payer, uint256 amount) internal {
if (amount == 0) return;
poolManager.sync(currency);
if (currency.isAddressZero()) {
poolManager.settle{value: amount}();
} else {
_pay(currency, payer, amount);
poolManager.settle();
}
}
/// @notice Abstract function for contracts to implement paying tokens to the poolManager
/// @dev The recipient of the payment should be the poolManager
/// @param token The token to settle. This is known not to be the native currency
/// @param payer The address who should pay tokens
/// @param amount The number of tokens to send
function _pay(Currency token, address payer, uint256 amount) internal virtual;
/// @notice Obtain the full amount owed by this contract (negative delta)
/// @param currency Currency to get the delta for
/// @return amount The amount owed by this contract as a uint256
function _getFullDebt(Currency currency) internal view returns (uint256 amount) {
int256 _amount = poolManager.currencyDelta(address(this), currency);
// If the amount is positive, it should be taken not settled.
if (_amount > 0) revert DeltaNotNegative(currency);
// Casting is safe due to limits on the total supply of a pool
amount = uint256(-_amount);
}
/// @notice Obtain the full credit owed to this contract (positive delta)
/// @param currency Currency to get the delta for
/// @return amount The amount owed to this contract as a uint256
function _getFullCredit(Currency currency) internal view returns (uint256 amount) {
int256 _amount = poolManager.currencyDelta(address(this), currency);
// If the amount is negative, it should be settled not taken.
if (_amount < 0) revert DeltaNotPositive(currency);
amount = uint256(_amount);
}
/// @notice Calculates the amount for a settle action
function _mapSettleAmount(uint256 amount, Currency currency) internal view returns (uint256) {
if (amount == ActionConstants.CONTRACT_BALANCE) {
return currency.balanceOfSelf();
} else if (amount == ActionConstants.OPEN_DELTA) {
return _getFullDebt(currency);
} else {
return amount;
}
}
/// @notice Calculates the amount for a take action
function _mapTakeAmount(uint256 amount, Currency currency) internal view returns (uint256) {
if (amount == ActionConstants.OPEN_DELTA) {
return _getFullCredit(currency);
} else {
return amount;
}
}
/// @notice Calculates the sanitized amount before wrapping/unwrapping.
/// @param inputCurrency The currency, either native or wrapped native, that this contract holds
/// @param amount The amount to wrap or unwrap. Can be CONTRACT_BALANCE, OPEN_DELTA or a specific amount
/// @param outputCurrency The currency after the wrap/unwrap that the user may owe a balance in on the poolManager
function _mapWrapUnwrapAmount(Currency inputCurrency, uint256 amount, Currency outputCurrency)
internal
view
returns (uint256)
{
// if wrapping, the balance in this contract is in ETH
// if unwrapping, the balance in this contract is in WETH
uint256 balance = inputCurrency.balanceOf(address(this));
if (amount == ActionConstants.CONTRACT_BALANCE) {
// return early to avoid unnecessary balance check
return balance;
}
if (amount == ActionConstants.OPEN_DELTA) {
// if wrapping, the open currency on the PoolManager is WETH.
// if unwrapping, the open currency on the PoolManager is ETH.
// note that we use the DEBT amount. Positive deltas can be taken and then wrapped.
amount = _getFullDebt(outputCurrency);
}
if (amount > balance) revert InsufficientBalance();
return amount;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @notice Library to define different pool actions.
/// @dev These are suggested common commands, however additional commands should be defined as required
/// Some of these actions are not supported in the Router contracts or Position Manager contracts, but are left as they may be helpful commands for other peripheral contracts.
library Actions {
// pool actions
// liquidity actions
uint256 internal constant INCREASE_LIQUIDITY = 0x00;
uint256 internal constant DECREASE_LIQUIDITY = 0x01;
uint256 internal constant MINT_POSITION = 0x02;
uint256 internal constant BURN_POSITION = 0x03;
uint256 internal constant INCREASE_LIQUIDITY_FROM_DELTAS = 0x04;
uint256 internal constant MINT_POSITION_FROM_DELTAS = 0x05;
// swapping
uint256 internal constant SWAP_EXACT_IN_SINGLE = 0x06;
uint256 internal constant SWAP_EXACT_IN = 0x07;
uint256 internal constant SWAP_EXACT_OUT_SINGLE = 0x08;
uint256 internal constant SWAP_EXACT_OUT = 0x09;
// donate
// note this is not supported in the position manager or router
uint256 internal constant DONATE = 0x0a;
// closing deltas on the pool manager
// settling
uint256 internal constant SETTLE = 0x0b;
uint256 internal constant SETTLE_ALL = 0x0c;
uint256 internal constant SETTLE_PAIR = 0x0d;
// taking
uint256 internal constant TAKE = 0x0e;
uint256 internal constant TAKE_ALL = 0x0f;
uint256 internal constant TAKE_PORTION = 0x10;
uint256 internal constant TAKE_PAIR = 0x11;
uint256 internal constant CLOSE_CURRENCY = 0x12;
uint256 internal constant CLEAR_OR_TAKE = 0x13;
uint256 internal constant SWEEP = 0x14;
uint256 internal constant WRAP = 0x15;
uint256 internal constant UNWRAP = 0x16;
// minting/burning 6909s to close deltas
// note this is not supported in the position manager or router
uint256 internal constant MINT_6909 = 0x17;
uint256 internal constant BURN_6909 = 0x18;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @title Minimal ERC20 interface for Uniswap
/// @notice Contains a subset of the full ERC20 interface that is used in Uniswap V3
interface IERC20Minimal {
/// @notice Returns an account's balance in the token
/// @param account The account for which to look up the number of tokens it has, i.e. its balance
/// @return The number of tokens held by the account
function balanceOf(address account) external view returns (uint256);
/// @notice Transfers the amount of token from the `msg.sender` to the recipient
/// @param recipient The account that will receive the amount transferred
/// @param amount The number of tokens to send from the sender to the recipient
/// @return Returns true for a successful transfer, false for an unsuccessful transfer
function transfer(address recipient, uint256 amount) external returns (bool);
/// @notice Returns the current allowance given to a spender by an owner
/// @param owner The account of the token owner
/// @param spender The account of the token spender
/// @return The current allowance granted by `owner` to `spender`
function allowance(address owner, address spender) external view returns (uint256);
/// @notice Sets the allowance of a spender from the `msg.sender` to the value `amount`
/// @param spender The account which will be allowed to spend a given amount of the owners tokens
/// @param amount The amount of tokens allowed to be used by `spender`
/// @return Returns true for a successful approval, false for unsuccessful
function approve(address spender, uint256 amount) external returns (bool);
/// @notice Transfers `amount` tokens from `sender` to `recipient` up to the allowance given to the `msg.sender`
/// @param sender The account from which the transfer will be initiated
/// @param recipient The recipient of the transfer
/// @param amount The amount of the transfer
/// @return Returns true for a successful transfer, false for unsuccessful
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
/// @notice Event emitted when tokens are transferred from one address to another, either via `#transfer` or `#transferFrom`.
/// @param from The account from which the tokens were sent, i.e. the balance decreased
/// @param to The account to which the tokens were sent, i.e. the balance increased
/// @param value The amount of tokens that were transferred
event Transfer(address indexed from, address indexed to, uint256 value);
/// @notice Event emitted when the approval amount for the spender of a given owner's tokens changes.
/// @param owner The account that approved spending of its tokens
/// @param spender The account for which the spending allowance was modified
/// @param value The new allowance from the owner to the spender
event Approval(address indexed owner, address indexed spender, uint256 value);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @title Library for reverting with custom errors efficiently
/// @notice Contains functions for reverting with custom errors with different argument types efficiently
/// @dev To use this library, declare `using CustomRevert for bytes4;` and replace `revert CustomError()` with
/// `CustomError.selector.revertWith()`
/// @dev The functions may tamper with the free memory pointer but it is fine since the call context is exited immediately
library CustomRevert {
/// @dev ERC-7751 error for wrapping bubbled up reverts
error WrappedError(address target, bytes4 selector, bytes reason, bytes details);
/// @dev Reverts with the selector of a custom error in the scratch space
function revertWith(bytes4 selector) internal pure {
assembly ("memory-safe") {
mstore(0, selector)
revert(0, 0x04)
}
}
/// @dev Reverts with a custom error with an address argument in the scratch space
function revertWith(bytes4 selector, address addr) internal pure {
assembly ("memory-safe") {
mstore(0, selector)
mstore(0x04, and(addr, 0xffffffffffffffffffffffffffffffffffffffff))
revert(0, 0x24)
}
}
/// @dev Reverts with a custom error with an int24 argument in the scratch space
function revertWith(bytes4 selector, int24 value) internal pure {
assembly ("memory-safe") {
mstore(0, selector)
mstore(0x04, signextend(2, value))
revert(0, 0x24)
}
}
/// @dev Reverts with a custom error with a uint160 argument in the scratch space
function revertWith(bytes4 selector, uint160 value) internal pure {
assembly ("memory-safe") {
mstore(0, selector)
mstore(0x04, and(value, 0xffffffffffffffffffffffffffffffffffffffff))
revert(0, 0x24)
}
}
/// @dev Reverts with a custom error with two int24 arguments
function revertWith(bytes4 selector, int24 value1, int24 value2) internal pure {
assembly ("memory-safe") {
let fmp := mload(0x40)
mstore(fmp, selector)
mstore(add(fmp, 0x04), signextend(2, value1))
mstore(add(fmp, 0x24), signextend(2, value2))
revert(fmp, 0x44)
}
}
/// @dev Reverts with a custom error with two uint160 arguments
function revertWith(bytes4 selector, uint160 value1, uint160 value2) internal pure {
assembly ("memory-safe") {
let fmp := mload(0x40)
mstore(fmp, selector)
mstore(add(fmp, 0x04), and(value1, 0xffffffffffffffffffffffffffffffffffffffff))
mstore(add(fmp, 0x24), and(value2, 0xffffffffffffffffffffffffffffffffffffffff))
revert(fmp, 0x44)
}
}
/// @dev Reverts with a custom error with two address arguments
function revertWith(bytes4 selector, address value1, address value2) internal pure {
assembly ("memory-safe") {
let fmp := mload(0x40)
mstore(fmp, selector)
mstore(add(fmp, 0x04), and(value1, 0xffffffffffffffffffffffffffffffffffffffff))
mstore(add(fmp, 0x24), and(value2, 0xffffffffffffffffffffffffffffffffffffffff))
revert(fmp, 0x44)
}
}
/// @notice bubble up the revert message returned by a call and revert with a wrapped ERC-7751 error
/// @dev this method can be vulnerable to revert data bombs
function bubbleUpAndRevertWith(
address revertingContract,
bytes4 revertingFunctionSelector,
bytes4 additionalContext
) internal pure {
bytes4 wrappedErrorSelector = WrappedError.selector;
assembly ("memory-safe") {
// Ensure the size of the revert data is a multiple of 32 bytes
let encodedDataSize := mul(div(add(returndatasize(), 31), 32), 32)
let fmp := mload(0x40)
// Encode wrapped error selector, address, function selector, offset, additional context, size, revert reason
mstore(fmp, wrappedErrorSelector)
mstore(add(fmp, 0x04), and(revertingContract, 0xffffffffffffffffffffffffffffffffffffffff))
mstore(
add(fmp, 0x24),
and(revertingFunctionSelector, 0xffffffff00000000000000000000000000000000000000000000000000000000)
)
// offset revert reason
mstore(add(fmp, 0x44), 0x80)
// offset additional context
mstore(add(fmp, 0x64), add(0xa0, encodedDataSize))
// size revert reason
mstore(add(fmp, 0x84), returndatasize())
// revert reason
returndatacopy(add(fmp, 0xa4), 0, returndatasize())
// size additional context
mstore(add(fmp, add(0xa4, encodedDataSize)), 0x04)
// additional context
mstore(
add(fmp, add(0xc4, encodedDataSize)),
and(additionalContext, 0xffffffff00000000000000000000000000000000000000000000000000000000)
)
revert(fmp, add(0xe4, encodedDataSize))
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol";
/// @title IImmutableState
/// @notice Interface for the ImmutableState contract
interface IImmutableState {
/// @notice The Uniswap v4 PoolManager contract
function poolManager() external view returns (IPoolManager);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Return type of the beforeSwap hook.
// Upper 128 bits is the delta in specified tokens. Lower 128 bits is delta in unspecified tokens (to match the afterSwap hook)
type BeforeSwapDelta is int256;
// Creates a BeforeSwapDelta from specified and unspecified
function toBeforeSwapDelta(int128 deltaSpecified, int128 deltaUnspecified)
pure
returns (BeforeSwapDelta beforeSwapDelta)
{
assembly ("memory-safe") {
beforeSwapDelta := or(shl(128, deltaSpecified), and(sub(shl(128, 1), 1), deltaUnspecified))
}
}
/// @notice Library for getting the specified and unspecified deltas from the BeforeSwapDelta type
library BeforeSwapDeltaLibrary {
/// @notice A BeforeSwapDelta of 0
BeforeSwapDelta public constant ZERO_DELTA = BeforeSwapDelta.wrap(0);
/// extracts int128 from the upper 128 bits of the BeforeSwapDelta
/// returned by beforeSwap
function getSpecifiedDelta(BeforeSwapDelta delta) internal pure returns (int128 deltaSpecified) {
assembly ("memory-safe") {
deltaSpecified := sar(128, delta)
}
}
/// extracts int128 from the lower 128 bits of the BeforeSwapDelta
/// returned by beforeSwap and afterSwap
function getUnspecifiedDelta(BeforeSwapDelta delta) internal pure returns (int128 deltaUnspecified) {
assembly ("memory-safe") {
deltaUnspecified := signextend(15, delta)
}
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.8.0;
// adapted from https://github.com/defi-wonderland/xERC20
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
interface IXERC20 is IERC20 {
/**
* @notice Mints tokens for a user
* @dev Can only be called by a minter
* @param _user The address of the user who needs tokens minted
* @param _amount The amount of tokens being minted
*/
function mint(address _user, uint256 _amount) external;
/**
* @notice Burns tokens for a user
* @dev Can only be called by a minter
* @param _user The address of the user who needs tokens burned
* @param _amount The amount of tokens being burned
*/
function burn(address _user, uint256 _amount) external;
/**
* @notice Updates the limits of any bridge
* @dev Can only be called by the owner
* @param _mintingLimit The updated minting limit we are setting to the bridge
* @param _burningLimit The updated burning limit we are setting to the bridge
* @param _bridge The address of the bridge we are setting the limits too
*/
function setLimits(
address _bridge,
uint256 _mintingLimit,
uint256 _burningLimit
) external;
function owner() external returns (address);
/**
* @notice Returns the current limit of a bridge
* @param _bridge the bridge we are viewing the limits of
* @return _limit The limit the bridge has
*/
function burningCurrentLimitOf(
address _bridge
) external view returns (uint256 _limit);
/**
* @notice Returns the current limit of a bridge
* @param _bridge the bridge we are viewing the limits of
* @return _limit The limit the bridge has
*/
function mintingCurrentLimitOf(
address _bridge
) external view returns (uint256 _limit);
/**
* @notice Returns the max limit of a minter
*
* @param _minter The minter we are viewing the limits of
* @return _limit The limit the minter has
*/
function mintingMaxLimitOf(
address _minter
) external view returns (uint256 _limit);
/**
* @notice Returns the max limit of a bridge
*
* @param _bridge the bridge we are viewing the limits of
* @return _limit The limit the bridge has
*/
function burningMaxLimitOf(
address _bridge
) external view returns (uint256 _limit);
}// SPDX-License-Identifier: Apache-2.0
pragma solidity >=0.8.0;
/*@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@ HYPERLANE @@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@*/
// ============ Internal Imports ============
import {TokenRouter} from "./libs/TokenRouter.sol";
import {TokenMessage} from "./libs/TokenMessage.sol";
import {MailboxClient} from "../client/MailboxClient.sol";
// ============ External Imports ============
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
/**
* @title Hyperlane ERC20 Token Collateral that wraps an existing ERC20 with remote transfer functionality.
* @author Abacus Works
*/
contract HypERC20Collateral is TokenRouter {
using SafeERC20 for IERC20;
IERC20 public immutable wrappedToken;
/**
* @notice Constructor
* @param erc20 Address of the token to keep as collateral
*/
constructor(address erc20, address _mailbox) TokenRouter(_mailbox) {
require(Address.isContract(erc20), "HypERC20Collateral: invalid token");
wrappedToken = IERC20(erc20);
}
function initialize(
address _hook,
address _interchainSecurityModule,
address _owner
) public virtual initializer {
_MailboxClient_initialize(_hook, _interchainSecurityModule, _owner);
}
function balanceOf(
address _account
) external view override returns (uint256) {
return wrappedToken.balanceOf(_account);
}
/**
* @dev Transfers `_amount` of `wrappedToken` from `msg.sender` to this contract.
* @inheritdoc TokenRouter
*/
function _transferFromSender(
uint256 _amount
) internal virtual override returns (bytes memory) {
wrappedToken.safeTransferFrom(msg.sender, address(this), _amount);
return bytes(""); // no metadata
}
/**
* @dev Transfers `_amount` of `wrappedToken` from this contract to `_recipient`.
* @inheritdoc TokenRouter
*/
function _transferTo(
address _recipient,
uint256 _amount,
bytes calldata // no metadata
) internal virtual override {
wrappedToken.safeTransfer(_recipient, _amount);
}
}// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity >=0.6.11;
interface IInterchainSecurityModule {
enum Types {
UNUSED,
ROUTING,
AGGREGATION,
LEGACY_MULTISIG,
MERKLE_ROOT_MULTISIG,
MESSAGE_ID_MULTISIG,
NULL, // used with relayer carrying no metadata
CCIP_READ,
ARB_L2_TO_L1,
WEIGHTED_MERKLE_ROOT_MULTISIG,
WEIGHTED_MESSAGE_ID_MULTISIG,
OP_L2_TO_L1
}
/**
* @notice Returns an enum that represents the type of security model
* encoded by this ISM.
* @dev Relayers infer how to fetch and format metadata.
*/
function moduleType() external view returns (uint8);
/**
* @notice Defines a security model responsible for verifying interchain
* messages based on the provided metadata.
* @param _metadata Off-chain metadata provided by a relayer, specific to
* the security model encoded by the module (e.g. validator signatures)
* @param _message Hyperlane encoded interchain message
* @return True if the message was verified
*/
function verify(
bytes calldata _metadata,
bytes calldata _message
) external returns (bool);
}
interface ISpecifiesInterchainSecurityModule {
function interchainSecurityModule()
external
view
returns (IInterchainSecurityModule);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @title BitMath
/// @dev This library provides functionality for computing bit properties of an unsigned integer
/// @author Solady (https://github.com/Vectorized/solady/blob/8200a70e8dc2a77ecb074fc2e99a2a0d36547522/src/utils/LibBit.sol)
library BitMath {
/// @notice Returns the index of the most significant bit of the number,
/// where the least significant bit is at index 0 and the most significant bit is at index 255
/// @param x the value for which to compute the most significant bit, must be greater than 0
/// @return r the index of the most significant bit
function mostSignificantBit(uint256 x) internal pure returns (uint8 r) {
require(x > 0);
assembly ("memory-safe") {
r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
r := or(r, shl(4, lt(0xffff, shr(r, x))))
r := or(r, shl(3, lt(0xff, shr(r, x))))
// forgefmt: disable-next-item
r := or(r, byte(and(0x1f, shr(shr(r, x), 0x8421084210842108cc6318c6db6d54be)),
0x0706060506020500060203020504000106050205030304010505030400000000))
}
}
/// @notice Returns the index of the least significant bit of the number,
/// where the least significant bit is at index 0 and the most significant bit is at index 255
/// @param x the value for which to compute the least significant bit, must be greater than 0
/// @return r the index of the least significant bit
function leastSignificantBit(uint256 x) internal pure returns (uint8 r) {
require(x > 0);
assembly ("memory-safe") {
// Isolate the least significant bit.
x := and(x, sub(0, x))
// For the upper 3 bits of the result, use a De Bruijn-like lookup.
// Credit to adhusson: https://blog.adhusson.com/cheap-find-first-set-evm/
// forgefmt: disable-next-item
r := shl(5, shr(252, shl(shl(2, shr(250, mul(x,
0xb6db6db6ddddddddd34d34d349249249210842108c6318c639ce739cffffffff))),
0x8040405543005266443200005020610674053026020000107506200176117077)))
// For the lower 5 bits of the result, use a De Bruijn lookup.
// forgefmt: disable-next-item
r := or(r, byte(and(div(0xd76453e0, shr(r, x)), 0x1f),
0x001f0d1e100c1d070f090b19131c1706010e11080a1a141802121b1503160405))
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {IUnlockCallback} from "@uniswap/v4-core/src/interfaces/callback/IUnlockCallback.sol";
import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol";
import {ImmutableState} from "./ImmutableState.sol";
/// @title Safe Callback
/// @notice A contract that only allows the Uniswap v4 PoolManager to call the unlockCallback
abstract contract SafeCallback is ImmutableState, IUnlockCallback {
constructor(IPoolManager _poolManager) ImmutableState(_poolManager) {}
/// @inheritdoc IUnlockCallback
/// @dev We force the onlyPoolManager modifier by exposing a virtual function after the onlyPoolManager check.
function unlockCallback(bytes calldata data) external onlyPoolManager returns (bytes memory) {
return _unlockCallback(data);
}
/// @dev to be implemented by the child contract, to safely guarantee the logic is only executed by the PoolManager
function _unlockCallback(bytes calldata data) internal virtual returns (bytes memory);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import {IPoolManager} from "../interfaces/IPoolManager.sol";
import {Currency} from "../types/Currency.sol";
import {CurrencyReserves} from "./CurrencyReserves.sol";
import {NonzeroDeltaCount} from "./NonzeroDeltaCount.sol";
import {Lock} from "./Lock.sol";
/// @notice A helper library to provide state getters that use exttload
library TransientStateLibrary {
/// @notice returns the reserves for the synced currency
/// @param manager The pool manager contract.
/// @return uint256 The reserves of the currency.
/// @dev returns 0 if the reserves are not synced or value is 0.
/// Checks the synced currency to only return valid reserve values (after a sync and before a settle).
function getSyncedReserves(IPoolManager manager) internal view returns (uint256) {
if (getSyncedCurrency(manager).isAddressZero()) return 0;
return uint256(manager.exttload(CurrencyReserves.RESERVES_OF_SLOT));
}
function getSyncedCurrency(IPoolManager manager) internal view returns (Currency) {
return Currency.wrap(address(uint160(uint256(manager.exttload(CurrencyReserves.CURRENCY_SLOT)))));
}
/// @notice Returns the number of nonzero deltas open on the PoolManager that must be zeroed out before the contract is locked
function getNonzeroDeltaCount(IPoolManager manager) internal view returns (uint256) {
return uint256(manager.exttload(NonzeroDeltaCount.NONZERO_DELTA_COUNT_SLOT));
}
/// @notice Get the current delta for a caller in the given currency
/// @param target The credited account address
/// @param currency The currency for which to lookup the delta
function currencyDelta(IPoolManager manager, address target, Currency currency) internal view returns (int256) {
bytes32 key;
assembly ("memory-safe") {
mstore(0, and(target, 0xffffffffffffffffffffffffffffffffffffffff))
mstore(32, and(currency, 0xffffffffffffffffffffffffffffffffffffffff))
key := keccak256(0, 64)
}
return int256(uint256(manager.exttload(key)));
}
/// @notice Returns whether the contract is unlocked or not
function isUnlocked(IPoolManager manager) internal view returns (bool) {
return manager.exttload(Lock.IS_UNLOCKED_SLOT) != 0x0;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol";
import {IImmutableState} from "../interfaces/IImmutableState.sol";
/// @title Immutable State
/// @notice A collection of immutable state variables, commonly used across multiple contracts
contract ImmutableState is IImmutableState {
/// @inheritdoc IImmutableState
IPoolManager public immutable poolManager;
/// @notice Thrown when the caller is not PoolManager
error NotPoolManager();
/// @notice Only allow calls from the PoolManager contract
modifier onlyPoolManager() {
if (msg.sender != address(poolManager)) revert NotPoolManager();
_;
}
constructor(IPoolManager _poolManager) {
poolManager = _poolManager;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @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);
/**
* @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);
}// SPDX-License-Identifier: Apache-2.0
pragma solidity >=0.8.0;
import {IPostDispatchHook} from "../../interfaces/hooks/IPostDispatchHook.sol";
import {GasRouter} from "../../client/GasRouter.sol";
import {MailboxClient} from "../../client/MailboxClient.sol";
import {TypeCasts} from "../../libs/TypeCasts.sol";
import {TokenMessage} from "./TokenMessage.sol";
/**
* @title Hyperlane Token Router that extends Router with abstract token (ERC20/ERC721) remote transfer functionality.
* @author Abacus Works
*/
abstract contract TokenRouter is GasRouter {
using TypeCasts for bytes32;
using TypeCasts for address;
using TokenMessage for bytes;
/**
* @dev Emitted on `transferRemote` when a transfer message is dispatched.
* @param destination The identifier of the destination chain.
* @param recipient The address of the recipient on the destination chain.
* @param amount The amount of tokens burnt on the origin chain.
*/
event SentTransferRemote(
uint32 indexed destination,
bytes32 indexed recipient,
uint256 amount
);
/**
* @dev Emitted on `_handle` when a transfer message is processed.
* @param origin The identifier of the origin chain.
* @param recipient The address of the recipient on the destination chain.
* @param amount The amount of tokens minted on the destination chain.
*/
event ReceivedTransferRemote(
uint32 indexed origin,
bytes32 indexed recipient,
uint256 amount
);
constructor(address _mailbox) GasRouter(_mailbox) {}
/**
* @notice Transfers `_amountOrId` token to `_recipient` on `_destination` domain.
* @dev Delegates transfer logic to `_transferFromSender` implementation.
* @dev Emits `SentTransferRemote` event on the origin chain.
* @param _destination The identifier of the destination chain.
* @param _recipient The address of the recipient on the destination chain.
* @param _amountOrId The amount or identifier of tokens to be sent to the remote recipient.
* @return messageId The identifier of the dispatched message.
*/
function transferRemote(
uint32 _destination,
bytes32 _recipient,
uint256 _amountOrId
) external payable virtual returns (bytes32 messageId) {
return
_transferRemote(_destination, _recipient, _amountOrId, msg.value);
}
/**
* @notice Transfers `_amountOrId` token to `_recipient` on `_destination` domain with a specified hook
* @dev Delegates transfer logic to `_transferFromSender` implementation.
* @dev The metadata is the token metadata, and is DIFFERENT than the hook metadata.
* @dev Emits `SentTransferRemote` event on the origin chain.
* @param _destination The identifier of the destination chain.
* @param _recipient The address of the recipient on the destination chain.
* @param _amountOrId The amount or identifier of tokens to be sent to the remote recipient.
* @param _hookMetadata The metadata passed into the hook
* @param _hook The post dispatch hook to be called by the Mailbox
* @return messageId The identifier of the dispatched message.
*/
function transferRemote(
uint32 _destination,
bytes32 _recipient,
uint256 _amountOrId,
bytes calldata _hookMetadata,
address _hook
) external payable virtual returns (bytes32 messageId) {
return
_transferRemote(
_destination,
_recipient,
_amountOrId,
msg.value,
_hookMetadata,
_hook
);
}
function _transferRemote(
uint32 _destination,
bytes32 _recipient,
uint256 _amountOrId,
uint256 _value
) internal returns (bytes32 messageId) {
return
_transferRemote(
_destination,
_recipient,
_amountOrId,
_value,
_GasRouter_hookMetadata(_destination),
address(hook)
);
}
function _transferRemote(
uint32 _destination,
bytes32 _recipient,
uint256 _amountOrId,
uint256 _value,
bytes memory _hookMetadata,
address _hook
) internal virtual returns (bytes32 messageId) {
bytes memory _tokenMetadata = _transferFromSender(_amountOrId);
bytes memory _tokenMessage = TokenMessage.format(
_recipient,
_amountOrId,
_tokenMetadata
);
messageId = _Router_dispatch(
_destination,
_value,
_tokenMessage,
_hookMetadata,
_hook
);
emit SentTransferRemote(_destination, _recipient, _amountOrId);
}
/**
* @dev Should transfer `_amountOrId` of tokens from `msg.sender` to this token router.
* @dev Called by `transferRemote` before message dispatch.
* @dev Optionally returns `metadata` associated with the transfer to be passed in message.
*/
function _transferFromSender(
uint256 _amountOrId
) internal virtual returns (bytes memory metadata);
/**
* @notice Returns the balance of `account` on this token router.
* @param account The address to query the balance of.
* @return The balance of `account`.
*/
function balanceOf(address account) external virtual returns (uint256);
/**
* @notice Returns the gas payment required to dispatch a message to the given domain's router.
* @param _destinationDomain The domain of the router.
* @return _gasPayment Payment computed by the registered InterchainGasPaymaster.
*/
function quoteGasPayment(
uint32 _destinationDomain
) external view override returns (uint256) {
return
_GasRouter_quoteDispatch(
_destinationDomain,
TokenMessage.format(bytes32(0), type(uint256).max, bytes("")),
address(hook)
);
}
/**
* @dev Mints tokens to recipient when router receives transfer message.
* @dev Emits `ReceivedTransferRemote` event on the destination chain.
* @param _origin The identifier of the origin chain.
* @param _message The encoded remote transfer message containing the recipient address and amount.
*/
function _handle(
uint32 _origin,
bytes32,
bytes calldata _message
) internal virtual override {
bytes32 recipient = _message.recipient();
uint256 amount = _message.amount();
bytes calldata metadata = _message.metadata();
_transferTo(recipient.bytes32ToAddress(), amount, metadata);
emit ReceivedTransferRemote(_origin, recipient, amount);
}
/**
* @dev Should transfer `_amountOrId` of tokens from this token router to `_recipient`.
* @dev Called by `handle` after message decoding.
* @dev Optionally handles `metadata` associated with transfer passed in message.
*/
function _transferTo(
address _recipient,
uint256 _amountOrId,
bytes calldata metadata
) internal virtual;
}// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity >=0.8.0;
library TokenMessage {
function format(
bytes32 _recipient,
uint256 _amount,
bytes memory _metadata
) internal pure returns (bytes memory) {
return abi.encodePacked(_recipient, _amount, _metadata);
}
function recipient(bytes calldata message) internal pure returns (bytes32) {
return bytes32(message[0:32]);
}
function amount(bytes calldata message) internal pure returns (uint256) {
return uint256(bytes32(message[32:64]));
}
// alias for ERC721
function tokenId(bytes calldata message) internal pure returns (uint256) {
return amount(message);
}
function metadata(
bytes calldata message
) internal pure returns (bytes calldata) {
return message[64:];
}
}// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity >=0.6.11;
/*@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@ HYPERLANE @@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@*/
// ============ Internal Imports ============
import {IMailbox} from "../interfaces/IMailbox.sol";
import {IPostDispatchHook} from "../interfaces/hooks/IPostDispatchHook.sol";
import {IInterchainSecurityModule} from "../interfaces/IInterchainSecurityModule.sol";
import {Message} from "../libs/Message.sol";
import {PackageVersioned} from "../PackageVersioned.sol";
// ============ External Imports ============
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
abstract contract MailboxClient is OwnableUpgradeable, PackageVersioned {
using Message for bytes;
event HookSet(address _hook);
event IsmSet(address _ism);
IMailbox public immutable mailbox;
uint32 public immutable localDomain;
IPostDispatchHook public hook;
IInterchainSecurityModule public interchainSecurityModule;
uint256[48] private __GAP; // gap for upgrade safety
// ============ Modifiers ============
modifier onlyContract(address _contract) {
require(
Address.isContract(_contract),
"MailboxClient: invalid mailbox"
);
_;
}
modifier onlyContractOrNull(address _contract) {
require(
Address.isContract(_contract) || _contract == address(0),
"MailboxClient: invalid contract setting"
);
_;
}
/**
* @notice Only accept messages from a Hyperlane Mailbox contract
*/
modifier onlyMailbox() {
require(
msg.sender == address(mailbox),
"MailboxClient: sender not mailbox"
);
_;
}
constructor(address _mailbox) onlyContract(_mailbox) {
mailbox = IMailbox(_mailbox);
localDomain = mailbox.localDomain();
_transferOwnership(msg.sender);
}
/**
* @notice Sets the address of the application's custom hook.
* @param _hook The address of the hook contract.
*/
function setHook(
address _hook
) public virtual onlyContractOrNull(_hook) onlyOwner {
hook = IPostDispatchHook(_hook);
emit HookSet(_hook);
}
/**
* @notice Sets the address of the application's custom interchain security module.
* @param _module The address of the interchain security module contract.
*/
function setInterchainSecurityModule(
address _module
) public onlyContractOrNull(_module) onlyOwner {
interchainSecurityModule = IInterchainSecurityModule(_module);
emit IsmSet(_module);
}
// ======== Initializer =========
function _MailboxClient_initialize(
address _hook,
address _interchainSecurityModule,
address _owner
) internal onlyInitializing {
__Ownable_init();
setHook(_hook);
setInterchainSecurityModule(_interchainSecurityModule);
_transferOwnership(_owner);
}
function _isLatestDispatched(bytes32 id) internal view returns (bool) {
return mailbox.latestDispatchedId() == id;
}
function _isDelivered(bytes32 id) internal view returns (bool) {
return mailbox.delivered(id);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.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
*
* Furthermore, `isContract` will also return true if the target contract within
* the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
* which only has an effect at the end of a transaction.
* ====
*
* [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://consensys.net/diligence/blog/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.8.0/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 functionCallWithValue(target, data, 0, "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");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, 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) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, 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) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
* the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
*
* _Available since v4.8._
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
// only check isContract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
/**
* @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason or 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 {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) private pure {
// 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
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.3) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.0;
import "../IERC20.sol";
import "../extensions/IERC20Permit.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;
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
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));
}
/**
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
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");
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
* to be set to zero before setting it to a non-zero value, such as USDT.
*/
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
* Revert on invalid signature.
*/
function safePermit(
IERC20Permit token,
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) internal {
uint256 nonceBefore = token.nonces(owner);
token.permit(owner, spender, value, deadline, v, r, s);
uint256 nonceAfter = token.nonces(owner);
require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
}
/**
* @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");
require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
/**
* @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).
*
* This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
// 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 cannot use {Address-functionCall} here since this should return false
// and not revert is the subcall reverts.
(bool success, bytes memory returndata) = address(token).call(data);
return
success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token));
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @notice Interface for the callback executed when an address unlocks the pool manager
interface IUnlockCallback {
/// @notice Called by the pool manager on `msg.sender` when the manager is unlocked
/// @param data The data that was passed to the call to unlock
/// @return Any data that you want to be returned from the unlock call
function unlockCallback(bytes calldata data) external returns (bytes memory);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;
import {Currency} from "../types/Currency.sol";
import {CustomRevert} from "./CustomRevert.sol";
library CurrencyReserves {
using CustomRevert for bytes4;
/// bytes32(uint256(keccak256("ReservesOf")) - 1)
bytes32 constant RESERVES_OF_SLOT = 0x1e0745a7db1623981f0b2a5d4232364c00787266eb75ad546f190e6cebe9bd95;
/// bytes32(uint256(keccak256("Currency")) - 1)
bytes32 constant CURRENCY_SLOT = 0x27e098c505d44ec3574004bca052aabf76bd35004c182099d8c575fb238593b9;
function getSyncedCurrency() internal view returns (Currency currency) {
assembly ("memory-safe") {
currency := tload(CURRENCY_SLOT)
}
}
function resetCurrency() internal {
assembly ("memory-safe") {
tstore(CURRENCY_SLOT, 0)
}
}
function syncCurrencyAndReserves(Currency currency, uint256 value) internal {
assembly ("memory-safe") {
tstore(CURRENCY_SLOT, and(currency, 0xffffffffffffffffffffffffffffffffffffffff))
tstore(RESERVES_OF_SLOT, value)
}
}
function getSyncedReserves() internal view returns (uint256 value) {
assembly ("memory-safe") {
value := tload(RESERVES_OF_SLOT)
}
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;
/// @notice This is a temporary library that allows us to use transient storage (tstore/tload)
/// for the nonzero delta count.
/// TODO: This library can be deleted when we have the transient keyword support in solidity.
library NonzeroDeltaCount {
// The slot holding the number of nonzero deltas. bytes32(uint256(keccak256("NonzeroDeltaCount")) - 1)
bytes32 internal constant NONZERO_DELTA_COUNT_SLOT =
0x7d4b3164c6e45b97e7d87b7125a44c5828d005af88f9d751cfd78729c5d99a0b;
function read() internal view returns (uint256 count) {
assembly ("memory-safe") {
count := tload(NONZERO_DELTA_COUNT_SLOT)
}
}
function increment() internal {
assembly ("memory-safe") {
let count := tload(NONZERO_DELTA_COUNT_SLOT)
count := add(count, 1)
tstore(NONZERO_DELTA_COUNT_SLOT, count)
}
}
/// @notice Potential to underflow. Ensure checks are performed by integrating contracts to ensure this does not happen.
/// Current usage ensures this will not happen because we call decrement with known boundaries (only up to the number of times we call increment).
function decrement() internal {
assembly ("memory-safe") {
let count := tload(NONZERO_DELTA_COUNT_SLOT)
count := sub(count, 1)
tstore(NONZERO_DELTA_COUNT_SLOT, count)
}
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;
/// @notice This is a temporary library that allows us to use transient storage (tstore/tload)
/// TODO: This library can be deleted when we have the transient keyword support in solidity.
library Lock {
// The slot holding the unlocked state, transiently. bytes32(uint256(keccak256("Unlocked")) - 1)
bytes32 internal constant IS_UNLOCKED_SLOT = 0xc090fc4683624cfc3884e9d8de5eca132f2d0ec062aff75d43c0465d5ceeab23;
function unlock() internal {
assembly ("memory-safe") {
// unlock
tstore(IS_UNLOCKED_SLOT, true)
}
}
function lock() internal {
assembly ("memory-safe") {
tstore(IS_UNLOCKED_SLOT, false)
}
}
function isUnlocked() internal view returns (bool unlocked) {
assembly ("memory-safe") {
unlocked := tload(IS_UNLOCKED_SLOT)
}
}
}// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity >=0.6.11;
/*@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@ HYPERLANE @@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@*/
// ============ Internal Imports ============
import {Router} from "./Router.sol";
import {StandardHookMetadata} from "../hooks/libs/StandardHookMetadata.sol";
abstract contract GasRouter is Router {
event GasSet(uint32 domain, uint256 gas);
// ============ Mutable Storage ============
mapping(uint32 => uint256) public destinationGas;
struct GasRouterConfig {
uint32 domain;
uint256 gas;
}
constructor(address _mailbox) Router(_mailbox) {}
/**
* @notice Sets the gas amount dispatched for each configured domain.
* @param gasConfigs The array of GasRouterConfig structs
*/
function setDestinationGas(
GasRouterConfig[] calldata gasConfigs
) external onlyOwner {
for (uint256 i = 0; i < gasConfigs.length; i += 1) {
_setDestinationGas(gasConfigs[i].domain, gasConfigs[i].gas);
}
}
/**
* @notice Sets the gas amount dispatched for each configured domain.
* @param domain The destination domain ID
* @param gas The gas limit
*/
function setDestinationGas(uint32 domain, uint256 gas) external onlyOwner {
_setDestinationGas(domain, gas);
}
/**
* @notice Returns the gas payment required to dispatch a message to the given domain's router.
* @param _destinationDomain The domain of the router.
* @return _gasPayment Payment computed by the registered InterchainGasPaymaster.
*/
function quoteGasPayment(
uint32 _destinationDomain
) external view virtual returns (uint256) {
return _GasRouter_quoteDispatch(_destinationDomain, "", address(hook));
}
function _GasRouter_hookMetadata(
uint32 _destination
) internal view returns (bytes memory) {
return
StandardHookMetadata.overrideGasLimit(destinationGas[_destination]);
}
function _setDestinationGas(uint32 domain, uint256 gas) internal {
destinationGas[domain] = gas;
emit GasSet(domain, gas);
}
function _GasRouter_dispatch(
uint32 _destination,
uint256 _value,
bytes memory _messageBody,
address _hook
) internal returns (bytes32) {
return
_Router_dispatch(
_destination,
_value,
_messageBody,
_GasRouter_hookMetadata(_destination),
_hook
);
}
function _GasRouter_quoteDispatch(
uint32 _destination,
bytes memory _messageBody,
address _hook
) internal view returns (uint256) {
return
_Router_quoteDispatch(
_destination,
_messageBody,
_GasRouter_hookMetadata(_destination),
_hook
);
}
}// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity >=0.8.0;
import {IInterchainSecurityModule} from "./IInterchainSecurityModule.sol";
import {IPostDispatchHook} from "./hooks/IPostDispatchHook.sol";
interface IMailbox {
// ============ Events ============
/**
* @notice Emitted when a new message is dispatched via Hyperlane
* @param sender The address that dispatched the message
* @param destination The destination domain of the message
* @param recipient The message recipient address on `destination`
* @param message Raw bytes of message
*/
event Dispatch(
address indexed sender,
uint32 indexed destination,
bytes32 indexed recipient,
bytes message
);
/**
* @notice Emitted when a new message is dispatched via Hyperlane
* @param messageId The unique message identifier
*/
event DispatchId(bytes32 indexed messageId);
/**
* @notice Emitted when a Hyperlane message is processed
* @param messageId The unique message identifier
*/
event ProcessId(bytes32 indexed messageId);
/**
* @notice Emitted when a Hyperlane message is delivered
* @param origin The origin domain of the message
* @param sender The message sender address on `origin`
* @param recipient The address that handled the message
*/
event Process(
uint32 indexed origin,
bytes32 indexed sender,
address indexed recipient
);
function localDomain() external view returns (uint32);
function delivered(bytes32 messageId) external view returns (bool);
function defaultIsm() external view returns (IInterchainSecurityModule);
function defaultHook() external view returns (IPostDispatchHook);
function requiredHook() external view returns (IPostDispatchHook);
function latestDispatchedId() external view returns (bytes32);
function dispatch(
uint32 destinationDomain,
bytes32 recipientAddress,
bytes calldata messageBody
) external payable returns (bytes32 messageId);
function quoteDispatch(
uint32 destinationDomain,
bytes32 recipientAddress,
bytes calldata messageBody
) external view returns (uint256 fee);
function dispatch(
uint32 destinationDomain,
bytes32 recipientAddress,
bytes calldata body,
bytes calldata defaultHookMetadata
) external payable returns (bytes32 messageId);
function quoteDispatch(
uint32 destinationDomain,
bytes32 recipientAddress,
bytes calldata messageBody,
bytes calldata defaultHookMetadata
) external view returns (uint256 fee);
function dispatch(
uint32 destinationDomain,
bytes32 recipientAddress,
bytes calldata body,
bytes calldata customHookMetadata,
IPostDispatchHook customHook
) external payable returns (bytes32 messageId);
function quoteDispatch(
uint32 destinationDomain,
bytes32 recipientAddress,
bytes calldata messageBody,
bytes calldata customHookMetadata,
IPostDispatchHook customHook
) external view returns (uint256 fee);
function process(
bytes calldata metadata,
bytes calldata message
) external payable;
function recipientIsm(
address recipient
) external view returns (IInterchainSecurityModule module);
}// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity >=0.8.0;
import {TypeCasts} from "./TypeCasts.sol";
/**
* @title Hyperlane Message Library
* @notice Library for formatted messages used by Mailbox
**/
library Message {
using TypeCasts for bytes32;
uint256 private constant VERSION_OFFSET = 0;
uint256 private constant NONCE_OFFSET = 1;
uint256 private constant ORIGIN_OFFSET = 5;
uint256 private constant SENDER_OFFSET = 9;
uint256 private constant DESTINATION_OFFSET = 41;
uint256 private constant RECIPIENT_OFFSET = 45;
uint256 private constant BODY_OFFSET = 77;
/**
* @notice Returns formatted (packed) Hyperlane message with provided fields
* @dev This function should only be used in memory message construction.
* @param _version The version of the origin and destination Mailboxes
* @param _nonce A nonce to uniquely identify the message on its origin chain
* @param _originDomain Domain of origin chain
* @param _sender Address of sender as bytes32
* @param _destinationDomain Domain of destination chain
* @param _recipient Address of recipient on destination chain as bytes32
* @param _messageBody Raw bytes of message body
* @return Formatted message
*/
function formatMessage(
uint8 _version,
uint32 _nonce,
uint32 _originDomain,
bytes32 _sender,
uint32 _destinationDomain,
bytes32 _recipient,
bytes calldata _messageBody
) internal pure returns (bytes memory) {
return
abi.encodePacked(
_version,
_nonce,
_originDomain,
_sender,
_destinationDomain,
_recipient,
_messageBody
);
}
/**
* @notice Returns the message ID.
* @param _message ABI encoded Hyperlane message.
* @return ID of `_message`
*/
function id(bytes memory _message) internal pure returns (bytes32) {
return keccak256(_message);
}
/**
* @notice Returns the message version.
* @param _message ABI encoded Hyperlane message.
* @return Version of `_message`
*/
function version(bytes calldata _message) internal pure returns (uint8) {
return uint8(bytes1(_message[VERSION_OFFSET:NONCE_OFFSET]));
}
/**
* @notice Returns the message nonce.
* @param _message ABI encoded Hyperlane message.
* @return Nonce of `_message`
*/
function nonce(bytes calldata _message) internal pure returns (uint32) {
return uint32(bytes4(_message[NONCE_OFFSET:ORIGIN_OFFSET]));
}
/**
* @notice Returns the message origin domain.
* @param _message ABI encoded Hyperlane message.
* @return Origin domain of `_message`
*/
function origin(bytes calldata _message) internal pure returns (uint32) {
return uint32(bytes4(_message[ORIGIN_OFFSET:SENDER_OFFSET]));
}
/**
* @notice Returns the message sender as bytes32.
* @param _message ABI encoded Hyperlane message.
* @return Sender of `_message` as bytes32
*/
function sender(bytes calldata _message) internal pure returns (bytes32) {
return bytes32(_message[SENDER_OFFSET:DESTINATION_OFFSET]);
}
/**
* @notice Returns the message sender as address.
* @param _message ABI encoded Hyperlane message.
* @return Sender of `_message` as address
*/
function senderAddress(
bytes calldata _message
) internal pure returns (address) {
return sender(_message).bytes32ToAddress();
}
/**
* @notice Returns the message destination domain.
* @param _message ABI encoded Hyperlane message.
* @return Destination domain of `_message`
*/
function destination(
bytes calldata _message
) internal pure returns (uint32) {
return uint32(bytes4(_message[DESTINATION_OFFSET:RECIPIENT_OFFSET]));
}
/**
* @notice Returns the message recipient as bytes32.
* @param _message ABI encoded Hyperlane message.
* @return Recipient of `_message` as bytes32
*/
function recipient(
bytes calldata _message
) internal pure returns (bytes32) {
return bytes32(_message[RECIPIENT_OFFSET:BODY_OFFSET]);
}
/**
* @notice Returns the message recipient as address.
* @param _message ABI encoded Hyperlane message.
* @return Recipient of `_message` as address
*/
function recipientAddress(
bytes calldata _message
) internal pure returns (address) {
return recipient(_message).bytes32ToAddress();
}
/**
* @notice Returns the message body.
* @param _message ABI encoded Hyperlane message.
* @return Body of `_message`
*/
function body(
bytes calldata _message
) internal pure returns (bytes calldata) {
return bytes(_message[BODY_OFFSET:]);
}
}// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity >=0.6.11;
/**
* @title PackageVersioned
* @notice Package version getter for contracts
**/
abstract contract PackageVersioned {
// GENERATED CODE - DO NOT EDIT
string public constant PACKAGE_VERSION = "5.12.0";
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)
pragma solidity ^0.8.0;
import "../utils/ContextUpgradeable.sol";
import {Initializable} from "../proxy/utils/Initializable.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 OwnableUpgradeable is Initializable, ContextUpgradeable {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
function __Ownable_init() internal onlyInitializing {
__Ownable_init_unchained();
}
function __Ownable_init_unchained() internal onlyInitializing {
_transferOwnership(_msgSender());
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling 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);
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[49] private __gap;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (token/ERC20/extensions/IERC20Permit.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
* presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
* need to send a transaction, and thus is not required to hold Ether at all.
*
* ==== Security Considerations
*
* There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
* expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
* considered as an intention to spend the allowance in any specific way. The second is that because permits have
* built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
* take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
* generally recommended is:
*
* ```solidity
* function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
* try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
* doThing(..., value);
* }
*
* function doThing(..., uint256 value) public {
* token.safeTransferFrom(msg.sender, address(this), value);
* ...
* }
* ```
*
* Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
* `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
* {SafeERC20-safeTransferFrom}).
*
* Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
* contracts should have entry points that don't rely on permit.
*/
interface IERC20Permit {
/**
* @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
* given ``owner``'s signed approval.
*
* IMPORTANT: The same issues {IERC20-approve} has related to transaction
* ordering also apply here.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `deadline` must be a timestamp in the future.
* - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
* over the EIP712-formatted function arguments.
* - the signature must use ``owner``'s current nonce (see {nonces}).
*
* For more information on the signature format, see the
* https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
* section].
*
* CAUTION: See Security Considerations above.
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @dev Returns the current nonce for `owner`. This value must be
* included whenever a signature is generated for {permit}.
*
* Every successful call to {permit} increases ``owner``'s nonce by one. This
* prevents a signature from being used multiple times.
*/
function nonces(address owner) external view returns (uint256);
/**
* @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view returns (bytes32);
}// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity >=0.6.11;
// ============ Internal Imports ============
import {IMessageRecipient} from "../interfaces/IMessageRecipient.sol";
import {IPostDispatchHook} from "../interfaces/hooks/IPostDispatchHook.sol";
import {IInterchainSecurityModule} from "../interfaces/IInterchainSecurityModule.sol";
import {MailboxClient} from "./MailboxClient.sol";
import {EnumerableMapExtended} from "../libs/EnumerableMapExtended.sol";
// ============ External Imports ============
import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
abstract contract Router is MailboxClient, IMessageRecipient {
using EnumerableMapExtended for EnumerableMapExtended.UintToBytes32Map;
using Strings for uint32;
// ============ Mutable Storage ============
EnumerableMapExtended.UintToBytes32Map internal _routers;
uint256[48] private __GAP; // gap for upgrade safety
constructor(address _mailbox) MailboxClient(_mailbox) {}
// ============ External functions ============
function domains() external view returns (uint32[] memory) {
return _routers.uint32Keys();
}
/**
* @notice Returns the address of the Router contract for the given domain
* @param _domain The remote domain ID.
* @dev Returns 0 address if no router is enrolled for the given domain
* @return router The address of the Router contract for the given domain
*/
function routers(uint32 _domain) public view virtual returns (bytes32) {
(, bytes32 _router) = _routers.tryGet(_domain);
return _router;
}
/**
* @notice Unregister the domain
* @param _domain The domain of the remote Application Router
*/
function unenrollRemoteRouter(uint32 _domain) external virtual onlyOwner {
_unenrollRemoteRouter(_domain);
}
/**
* @notice Register the address of a Router contract for the same Application on a remote chain
* @param _domain The domain of the remote Application Router
* @param _router The address of the remote Application Router
*/
function enrollRemoteRouter(
uint32 _domain,
bytes32 _router
) external virtual onlyOwner {
_enrollRemoteRouter(_domain, _router);
}
/**
* @notice Batch version of `enrollRemoteRouter`
* @param _domains The domains of the remote Application Routers
* @param _addresses The addresses of the remote Application Routers
*/
function enrollRemoteRouters(
uint32[] calldata _domains,
bytes32[] calldata _addresses
) external virtual onlyOwner {
require(_domains.length == _addresses.length, "!length");
uint256 length = _domains.length;
for (uint256 i = 0; i < length; i += 1) {
_enrollRemoteRouter(_domains[i], _addresses[i]);
}
}
/**
* @notice Batch version of `unenrollRemoteRouter`
* @param _domains The domains of the remote Application Routers
*/
function unenrollRemoteRouters(
uint32[] calldata _domains
) external virtual onlyOwner {
uint256 length = _domains.length;
for (uint256 i = 0; i < length; i += 1) {
_unenrollRemoteRouter(_domains[i]);
}
}
/**
* @notice Handles an incoming message
* @param _origin The origin domain
* @param _sender The sender address
* @param _message The message
*/
function handle(
uint32 _origin,
bytes32 _sender,
bytes calldata _message
) external payable virtual override onlyMailbox {
bytes32 _router = _mustHaveRemoteRouter(_origin);
require(_router == _sender, "Enrolled router does not match sender");
_handle(_origin, _sender, _message);
}
// ============ Virtual functions ============
function _handle(
uint32 _origin,
bytes32 _sender,
bytes calldata _message
) internal virtual;
// ============ Internal functions ============
/**
* @notice Set the router for a given domain
* @param _domain The domain
* @param _address The new router
*/
function _enrollRemoteRouter(
uint32 _domain,
bytes32 _address
) internal virtual {
_routers.set(_domain, _address);
}
/**
* @notice Remove the router for a given domain
* @param _domain The domain
*/
function _unenrollRemoteRouter(uint32 _domain) internal virtual {
require(_routers.remove(_domain), _domainNotFoundError(_domain));
}
/**
* @notice Return true if the given domain / router is the address of a remote Application Router
* @param _domain The domain of the potential remote Application Router
* @param _address The address of the potential remote Application Router
*/
function _isRemoteRouter(
uint32 _domain,
bytes32 _address
) internal view returns (bool) {
return routers(_domain) == _address;
}
/**
* @notice Assert that the given domain has an Application Router registered and return its address
* @param _domain The domain of the chain for which to get the Application Router
* @return _router The address of the remote Application Router on _domain
*/
function _mustHaveRemoteRouter(
uint32 _domain
) internal view returns (bytes32) {
(bool contained, bytes32 _router) = _routers.tryGet(_domain);
if (contained) {
return _router;
}
revert(_domainNotFoundError(_domain));
}
function _domainNotFoundError(
uint32 _domain
) internal pure returns (string memory) {
return
string.concat(
"No router enrolled for domain: ",
_domain.toString()
);
}
function _Router_dispatch(
uint32 _destinationDomain,
uint256 _value,
bytes memory _messageBody,
bytes memory _hookMetadata,
address _hook
) internal returns (bytes32) {
bytes32 _router = _mustHaveRemoteRouter(_destinationDomain);
return
mailbox.dispatch{value: _value}(
_destinationDomain,
_router,
_messageBody,
_hookMetadata,
IPostDispatchHook(_hook)
);
}
/**
* DEPRECATED: Use `_Router_dispatch` instead
* @dev For backward compatibility with v2 client contracts
*/
function _dispatch(
uint32 _destinationDomain,
bytes memory _messageBody
) internal returns (bytes32) {
return
_Router_dispatch(
_destinationDomain,
msg.value,
_messageBody,
"",
address(hook)
);
}
function _Router_quoteDispatch(
uint32 _destinationDomain,
bytes memory _messageBody,
bytes memory _hookMetadata,
address _hook
) internal view returns (uint256) {
bytes32 _router = _mustHaveRemoteRouter(_destinationDomain);
return
mailbox.quoteDispatch(
_destinationDomain,
_router,
_messageBody,
_hookMetadata,
IPostDispatchHook(_hook)
);
}
/**
* DEPRECATED: Use `_Router_quoteDispatch` instead
* @dev For backward compatibility with v2 client contracts
*/
function _quoteDispatch(
uint32 _destinationDomain,
bytes memory _messageBody
) internal view returns (uint256) {
return
_Router_quoteDispatch(
_destinationDomain,
_messageBody,
"",
address(hook)
);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (utils/Context.sol)
pragma solidity ^0.8.0;
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @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 ContextUpgradeable is Initializable {
function __Context_init() internal onlyInitializing {
}
function __Context_init_unchained() internal onlyInitializing {
}
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[50] private __gap;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (proxy/utils/Initializable.sol)
pragma solidity ^0.8.2;
import "../../utils/AddressUpgradeable.sol";
/**
* @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
* behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
* external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
* function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
*
* The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
* reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
* case an upgrade adds a module that needs to be initialized.
*
* For example:
*
* [.hljs-theme-light.nopadding]
* ```solidity
* contract MyToken is ERC20Upgradeable {
* function initialize() initializer public {
* __ERC20_init("MyToken", "MTK");
* }
* }
*
* contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
* function initializeV2() reinitializer(2) public {
* __ERC20Permit_init("MyToken");
* }
* }
* ```
*
* TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
* possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
*
* CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
* that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
*
* [CAUTION]
* ====
* Avoid leaving a contract uninitialized.
*
* An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
* contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
* the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
*
* [.hljs-theme-light.nopadding]
* ```
* /// @custom:oz-upgrades-unsafe-allow constructor
* constructor() {
* _disableInitializers();
* }
* ```
* ====
*/
abstract contract Initializable {
/**
* @dev Indicates that the contract has been initialized.
* @custom:oz-retyped-from bool
*/
uint8 private _initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/
bool private _initializing;
/**
* @dev Triggered when the contract has been initialized or reinitialized.
*/
event Initialized(uint8 version);
/**
* @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
* `onlyInitializing` functions can be used to initialize parent contracts.
*
* Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a
* constructor.
*
* Emits an {Initialized} event.
*/
modifier initializer() {
bool isTopLevelCall = !_initializing;
require(
(isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),
"Initializable: contract is already initialized"
);
_initialized = 1;
if (isTopLevelCall) {
_initializing = true;
}
_;
if (isTopLevelCall) {
_initializing = false;
emit Initialized(1);
}
}
/**
* @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
* contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
* used to initialize parent contracts.
*
* A reinitializer may be used after the original initialization step. This is essential to configure modules that
* are added through upgrades and that require initialization.
*
* When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
* cannot be nested. If one is invoked in the context of another, execution will revert.
*
* Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
* a contract, executing them in the right order is up to the developer or operator.
*
* WARNING: setting the version to 255 will prevent any future reinitialization.
*
* Emits an {Initialized} event.
*/
modifier reinitializer(uint8 version) {
require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
_initialized = version;
_initializing = true;
_;
_initializing = false;
emit Initialized(version);
}
/**
* @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
* {initializer} and {reinitializer} modifiers, directly or indirectly.
*/
modifier onlyInitializing() {
require(_initializing, "Initializable: contract is not initializing");
_;
}
/**
* @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
* Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
* to any version. It is recommended to use this to lock implementation contracts that are designed to be called
* through proxies.
*
* Emits an {Initialized} event the first time it is successfully executed.
*/
function _disableInitializers() internal virtual {
require(!_initializing, "Initializable: contract is initializing");
if (_initialized != type(uint8).max) {
_initialized = type(uint8).max;
emit Initialized(type(uint8).max);
}
}
/**
* @dev Returns the highest version that has been initialized. See {reinitializer}.
*/
function _getInitializedVersion() internal view returns (uint8) {
return _initialized;
}
/**
* @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
*/
function _isInitializing() internal view returns (bool) {
return _initializing;
}
}// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity >=0.6.11;
interface IMessageRecipient {
function handle(
uint32 _origin,
bytes32 _sender,
bytes calldata _message
) external payable;
}// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity >=0.6.11;
// ============ External Imports ============
import "@openzeppelin/contracts/utils/structs/EnumerableMap.sol";
import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
// extends EnumerableMap with uint256 => bytes32 type
// modelled after https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.8.0/contracts/utils/structs/EnumerableMap.sol
library EnumerableMapExtended {
using EnumerableMap for EnumerableMap.Bytes32ToBytes32Map;
using EnumerableSet for EnumerableSet.Bytes32Set;
struct UintToBytes32Map {
EnumerableMap.Bytes32ToBytes32Map _inner;
}
// ============ Library Functions ============
function keys(
UintToBytes32Map storage map
) internal view returns (uint256[] memory _keys) {
uint256 _length = map._inner.length();
_keys = new uint256[](_length);
for (uint256 i = 0; i < _length; i++) {
_keys[i] = uint256(map._inner._keys.at(i));
}
}
function uint32Keys(
UintToBytes32Map storage map
) internal view returns (uint32[] memory _keys) {
uint256[] memory uint256keys = keys(map);
_keys = new uint32[](uint256keys.length);
for (uint256 i = 0; i < uint256keys.length; i++) {
_keys[i] = uint32(uint256keys[i]);
}
}
function set(
UintToBytes32Map storage map,
uint256 key,
bytes32 value
) internal {
map._inner.set(bytes32(key), value);
}
function get(
UintToBytes32Map storage map,
uint256 key
) internal view returns (bytes32) {
return map._inner.get(bytes32(key));
}
function tryGet(
UintToBytes32Map storage map,
uint256 key
) internal view returns (bool, bytes32) {
return map._inner.tryGet(bytes32(key));
}
function remove(
UintToBytes32Map storage map,
uint256 key
) internal returns (bool) {
return map._inner.remove(bytes32(key));
}
function contains(
UintToBytes32Map storage map,
uint256 key
) internal view returns (bool) {
return map._inner.contains(bytes32(key));
}
function length(
UintToBytes32Map storage map
) internal view returns (uint256) {
return map._inner.length();
}
function at(
UintToBytes32Map storage map,
uint256 index
) internal view returns (uint256, bytes32) {
(bytes32 key, bytes32 value) = map._inner.at(index);
return (uint256(key), value);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol)
pragma solidity ^0.8.0;
import "./math/Math.sol";
import "./math/SignedMath.sol";
/**
* @dev String operations.
*/
library Strings {
bytes16 private constant _SYMBOLS = "0123456789abcdef";
uint8 private constant _ADDRESS_LENGTH = 20;
/**
* @dev Converts a `uint256` to its ASCII `string` decimal representation.
*/
function toString(uint256 value) internal pure returns (string memory) {
unchecked {
uint256 length = Math.log10(value) + 1;
string memory buffer = new string(length);
uint256 ptr;
/// @solidity memory-safe-assembly
assembly {
ptr := add(buffer, add(32, length))
}
while (true) {
ptr--;
/// @solidity memory-safe-assembly
assembly {
mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
}
value /= 10;
if (value == 0) break;
}
return buffer;
}
}
/**
* @dev Converts a `int256` to its ASCII `string` decimal representation.
*/
function toString(int256 value) internal pure returns (string memory) {
return string(abi.encodePacked(value < 0 ? "-" : "", toString(SignedMath.abs(value))));
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
*/
function toHexString(uint256 value) internal pure returns (string memory) {
unchecked {
return toHexString(value, Math.log256(value) + 1);
}
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
*/
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = _SYMBOLS[value & 0xf];
value >>= 4;
}
require(value == 0, "Strings: hex length insufficient");
return string(buffer);
}
/**
* @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
*/
function toHexString(address addr) internal pure returns (string memory) {
return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
}
/**
* @dev Returns true if the two strings are equal.
*/
function equal(string memory a, string memory b) internal pure returns (bool) {
return keccak256(bytes(a)) == keccak256(bytes(b));
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)
pragma solidity ^0.8.1;
/**
* @dev Collection of functions related to the address type
*/
library AddressUpgradeable {
/**
* @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
*
* Furthermore, `isContract` will also return true if the target contract within
* the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
* which only has an effect at the end of a transaction.
* ====
*
* [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://consensys.net/diligence/blog/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.8.0/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 functionCallWithValue(target, data, 0, "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");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, 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) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, 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) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
* the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
*
* _Available since v4.8._
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
// only check isContract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
/**
* @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason or 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 {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) private pure {
// 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
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/structs/EnumerableMap.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableMap.js.
pragma solidity ^0.8.0;
import "./EnumerableSet.sol";
/**
* @dev Library for managing an enumerable variant of Solidity's
* https://solidity.readthedocs.io/en/latest/types.html#mapping-types[`mapping`]
* type.
*
* Maps have the following properties:
*
* - Entries are added, removed, and checked for existence in constant time
* (O(1)).
* - Entries are enumerated in O(n). No guarantees are made on the ordering.
*
* ```solidity
* contract Example {
* // Add the library methods
* using EnumerableMap for EnumerableMap.UintToAddressMap;
*
* // Declare a set state variable
* EnumerableMap.UintToAddressMap private myMap;
* }
* ```
*
* The following map types are supported:
*
* - `uint256 -> address` (`UintToAddressMap`) since v3.0.0
* - `address -> uint256` (`AddressToUintMap`) since v4.6.0
* - `bytes32 -> bytes32` (`Bytes32ToBytes32Map`) since v4.6.0
* - `uint256 -> uint256` (`UintToUintMap`) since v4.7.0
* - `bytes32 -> uint256` (`Bytes32ToUintMap`) since v4.7.0
*
* [WARNING]
* ====
* Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
* unusable.
* See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
*
* In order to clean an EnumerableMap, you can either remove all elements one by one or create a fresh instance using an
* array of EnumerableMap.
* ====
*/
library EnumerableMap {
using EnumerableSet for EnumerableSet.Bytes32Set;
// To implement this library for multiple types with as little code
// repetition as possible, we write it in terms of a generic Map type with
// bytes32 keys and values.
// The Map implementation uses private functions, and user-facing
// implementations (such as Uint256ToAddressMap) are just wrappers around
// the underlying Map.
// This means that we can only create new EnumerableMaps for types that fit
// in bytes32.
struct Bytes32ToBytes32Map {
// Storage of keys
EnumerableSet.Bytes32Set _keys;
mapping(bytes32 => bytes32) _values;
}
/**
* @dev Adds a key-value pair to a map, or updates the value for an existing
* key. O(1).
*
* Returns true if the key was added to the map, that is if it was not
* already present.
*/
function set(Bytes32ToBytes32Map storage map, bytes32 key, bytes32 value) internal returns (bool) {
map._values[key] = value;
return map._keys.add(key);
}
/**
* @dev Removes a key-value pair from a map. O(1).
*
* Returns true if the key was removed from the map, that is if it was present.
*/
function remove(Bytes32ToBytes32Map storage map, bytes32 key) internal returns (bool) {
delete map._values[key];
return map._keys.remove(key);
}
/**
* @dev Returns true if the key is in the map. O(1).
*/
function contains(Bytes32ToBytes32Map storage map, bytes32 key) internal view returns (bool) {
return map._keys.contains(key);
}
/**
* @dev Returns the number of key-value pairs in the map. O(1).
*/
function length(Bytes32ToBytes32Map storage map) internal view returns (uint256) {
return map._keys.length();
}
/**
* @dev Returns the key-value pair stored at position `index` in the map. O(1).
*
* Note that there are no guarantees on the ordering of entries inside the
* array, and it may change when more entries are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(Bytes32ToBytes32Map storage map, uint256 index) internal view returns (bytes32, bytes32) {
bytes32 key = map._keys.at(index);
return (key, map._values[key]);
}
/**
* @dev Tries to returns the value associated with `key`. O(1).
* Does not revert if `key` is not in the map.
*/
function tryGet(Bytes32ToBytes32Map storage map, bytes32 key) internal view returns (bool, bytes32) {
bytes32 value = map._values[key];
if (value == bytes32(0)) {
return (contains(map, key), bytes32(0));
} else {
return (true, value);
}
}
/**
* @dev Returns the value associated with `key`. O(1).
*
* Requirements:
*
* - `key` must be in the map.
*/
function get(Bytes32ToBytes32Map storage map, bytes32 key) internal view returns (bytes32) {
bytes32 value = map._values[key];
require(value != 0 || contains(map, key), "EnumerableMap: nonexistent key");
return value;
}
/**
* @dev Same as {get}, with a custom error message when `key` is not in the map.
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {tryGet}.
*/
function get(
Bytes32ToBytes32Map storage map,
bytes32 key,
string memory errorMessage
) internal view returns (bytes32) {
bytes32 value = map._values[key];
require(value != 0 || contains(map, key), errorMessage);
return value;
}
/**
* @dev Return the an array containing all the keys
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function keys(Bytes32ToBytes32Map storage map) internal view returns (bytes32[] memory) {
return map._keys.values();
}
// UintToUintMap
struct UintToUintMap {
Bytes32ToBytes32Map _inner;
}
/**
* @dev Adds a key-value pair to a map, or updates the value for an existing
* key. O(1).
*
* Returns true if the key was added to the map, that is if it was not
* already present.
*/
function set(UintToUintMap storage map, uint256 key, uint256 value) internal returns (bool) {
return set(map._inner, bytes32(key), bytes32(value));
}
/**
* @dev Removes a value from a map. O(1).
*
* Returns true if the key was removed from the map, that is if it was present.
*/
function remove(UintToUintMap storage map, uint256 key) internal returns (bool) {
return remove(map._inner, bytes32(key));
}
/**
* @dev Returns true if the key is in the map. O(1).
*/
function contains(UintToUintMap storage map, uint256 key) internal view returns (bool) {
return contains(map._inner, bytes32(key));
}
/**
* @dev Returns the number of elements in the map. O(1).
*/
function length(UintToUintMap storage map) internal view returns (uint256) {
return length(map._inner);
}
/**
* @dev Returns the element stored at position `index` in the map. O(1).
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(UintToUintMap storage map, uint256 index) internal view returns (uint256, uint256) {
(bytes32 key, bytes32 value) = at(map._inner, index);
return (uint256(key), uint256(value));
}
/**
* @dev Tries to returns the value associated with `key`. O(1).
* Does not revert if `key` is not in the map.
*/
function tryGet(UintToUintMap storage map, uint256 key) internal view returns (bool, uint256) {
(bool success, bytes32 value) = tryGet(map._inner, bytes32(key));
return (success, uint256(value));
}
/**
* @dev Returns the value associated with `key`. O(1).
*
* Requirements:
*
* - `key` must be in the map.
*/
function get(UintToUintMap storage map, uint256 key) internal view returns (uint256) {
return uint256(get(map._inner, bytes32(key)));
}
/**
* @dev Same as {get}, with a custom error message when `key` is not in the map.
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {tryGet}.
*/
function get(UintToUintMap storage map, uint256 key, string memory errorMessage) internal view returns (uint256) {
return uint256(get(map._inner, bytes32(key), errorMessage));
}
/**
* @dev Return the an array containing all the keys
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function keys(UintToUintMap storage map) internal view returns (uint256[] memory) {
bytes32[] memory store = keys(map._inner);
uint256[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// UintToAddressMap
struct UintToAddressMap {
Bytes32ToBytes32Map _inner;
}
/**
* @dev Adds a key-value pair to a map, or updates the value for an existing
* key. O(1).
*
* Returns true if the key was added to the map, that is if it was not
* already present.
*/
function set(UintToAddressMap storage map, uint256 key, address value) internal returns (bool) {
return set(map._inner, bytes32(key), bytes32(uint256(uint160(value))));
}
/**
* @dev Removes a value from a map. O(1).
*
* Returns true if the key was removed from the map, that is if it was present.
*/
function remove(UintToAddressMap storage map, uint256 key) internal returns (bool) {
return remove(map._inner, bytes32(key));
}
/**
* @dev Returns true if the key is in the map. O(1).
*/
function contains(UintToAddressMap storage map, uint256 key) internal view returns (bool) {
return contains(map._inner, bytes32(key));
}
/**
* @dev Returns the number of elements in the map. O(1).
*/
function length(UintToAddressMap storage map) internal view returns (uint256) {
return length(map._inner);
}
/**
* @dev Returns the element stored at position `index` in the map. O(1).
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(UintToAddressMap storage map, uint256 index) internal view returns (uint256, address) {
(bytes32 key, bytes32 value) = at(map._inner, index);
return (uint256(key), address(uint160(uint256(value))));
}
/**
* @dev Tries to returns the value associated with `key`. O(1).
* Does not revert if `key` is not in the map.
*/
function tryGet(UintToAddressMap storage map, uint256 key) internal view returns (bool, address) {
(bool success, bytes32 value) = tryGet(map._inner, bytes32(key));
return (success, address(uint160(uint256(value))));
}
/**
* @dev Returns the value associated with `key`. O(1).
*
* Requirements:
*
* - `key` must be in the map.
*/
function get(UintToAddressMap storage map, uint256 key) internal view returns (address) {
return address(uint160(uint256(get(map._inner, bytes32(key)))));
}
/**
* @dev Same as {get}, with a custom error message when `key` is not in the map.
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {tryGet}.
*/
function get(
UintToAddressMap storage map,
uint256 key,
string memory errorMessage
) internal view returns (address) {
return address(uint160(uint256(get(map._inner, bytes32(key), errorMessage))));
}
/**
* @dev Return the an array containing all the keys
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function keys(UintToAddressMap storage map) internal view returns (uint256[] memory) {
bytes32[] memory store = keys(map._inner);
uint256[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// AddressToUintMap
struct AddressToUintMap {
Bytes32ToBytes32Map _inner;
}
/**
* @dev Adds a key-value pair to a map, or updates the value for an existing
* key. O(1).
*
* Returns true if the key was added to the map, that is if it was not
* already present.
*/
function set(AddressToUintMap storage map, address key, uint256 value) internal returns (bool) {
return set(map._inner, bytes32(uint256(uint160(key))), bytes32(value));
}
/**
* @dev Removes a value from a map. O(1).
*
* Returns true if the key was removed from the map, that is if it was present.
*/
function remove(AddressToUintMap storage map, address key) internal returns (bool) {
return remove(map._inner, bytes32(uint256(uint160(key))));
}
/**
* @dev Returns true if the key is in the map. O(1).
*/
function contains(AddressToUintMap storage map, address key) internal view returns (bool) {
return contains(map._inner, bytes32(uint256(uint160(key))));
}
/**
* @dev Returns the number of elements in the map. O(1).
*/
function length(AddressToUintMap storage map) internal view returns (uint256) {
return length(map._inner);
}
/**
* @dev Returns the element stored at position `index` in the map. O(1).
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(AddressToUintMap storage map, uint256 index) internal view returns (address, uint256) {
(bytes32 key, bytes32 value) = at(map._inner, index);
return (address(uint160(uint256(key))), uint256(value));
}
/**
* @dev Tries to returns the value associated with `key`. O(1).
* Does not revert if `key` is not in the map.
*/
function tryGet(AddressToUintMap storage map, address key) internal view returns (bool, uint256) {
(bool success, bytes32 value) = tryGet(map._inner, bytes32(uint256(uint160(key))));
return (success, uint256(value));
}
/**
* @dev Returns the value associated with `key`. O(1).
*
* Requirements:
*
* - `key` must be in the map.
*/
function get(AddressToUintMap storage map, address key) internal view returns (uint256) {
return uint256(get(map._inner, bytes32(uint256(uint160(key)))));
}
/**
* @dev Same as {get}, with a custom error message when `key` is not in the map.
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {tryGet}.
*/
function get(
AddressToUintMap storage map,
address key,
string memory errorMessage
) internal view returns (uint256) {
return uint256(get(map._inner, bytes32(uint256(uint160(key))), errorMessage));
}
/**
* @dev Return the an array containing all the keys
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function keys(AddressToUintMap storage map) internal view returns (address[] memory) {
bytes32[] memory store = keys(map._inner);
address[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// Bytes32ToUintMap
struct Bytes32ToUintMap {
Bytes32ToBytes32Map _inner;
}
/**
* @dev Adds a key-value pair to a map, or updates the value for an existing
* key. O(1).
*
* Returns true if the key was added to the map, that is if it was not
* already present.
*/
function set(Bytes32ToUintMap storage map, bytes32 key, uint256 value) internal returns (bool) {
return set(map._inner, key, bytes32(value));
}
/**
* @dev Removes a value from a map. O(1).
*
* Returns true if the key was removed from the map, that is if it was present.
*/
function remove(Bytes32ToUintMap storage map, bytes32 key) internal returns (bool) {
return remove(map._inner, key);
}
/**
* @dev Returns true if the key is in the map. O(1).
*/
function contains(Bytes32ToUintMap storage map, bytes32 key) internal view returns (bool) {
return contains(map._inner, key);
}
/**
* @dev Returns the number of elements in the map. O(1).
*/
function length(Bytes32ToUintMap storage map) internal view returns (uint256) {
return length(map._inner);
}
/**
* @dev Returns the element stored at position `index` in the map. O(1).
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(Bytes32ToUintMap storage map, uint256 index) internal view returns (bytes32, uint256) {
(bytes32 key, bytes32 value) = at(map._inner, index);
return (key, uint256(value));
}
/**
* @dev Tries to returns the value associated with `key`. O(1).
* Does not revert if `key` is not in the map.
*/
function tryGet(Bytes32ToUintMap storage map, bytes32 key) internal view returns (bool, uint256) {
(bool success, bytes32 value) = tryGet(map._inner, key);
return (success, uint256(value));
}
/**
* @dev Returns the value associated with `key`. O(1).
*
* Requirements:
*
* - `key` must be in the map.
*/
function get(Bytes32ToUintMap storage map, bytes32 key) internal view returns (uint256) {
return uint256(get(map._inner, key));
}
/**
* @dev Same as {get}, with a custom error message when `key` is not in the map.
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {tryGet}.
*/
function get(
Bytes32ToUintMap storage map,
bytes32 key,
string memory errorMessage
) internal view returns (uint256) {
return uint256(get(map._inner, key, errorMessage));
}
/**
* @dev Return the an array containing all the keys
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function keys(Bytes32ToUintMap storage map) internal view returns (bytes32[] memory) {
bytes32[] memory store = keys(map._inner);
bytes32[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.
pragma solidity ^0.8.0;
/**
* @dev Library for managing
* https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
* types.
*
* Sets have the following properties:
*
* - Elements are added, removed, and checked for existence in constant time
* (O(1)).
* - Elements are enumerated in O(n). No guarantees are made on the ordering.
*
* ```solidity
* contract Example {
* // Add the library methods
* using EnumerableSet for EnumerableSet.AddressSet;
*
* // Declare a set state variable
* EnumerableSet.AddressSet private mySet;
* }
* ```
*
* As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
* and `uint256` (`UintSet`) are supported.
*
* [WARNING]
* ====
* Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
* unusable.
* See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
*
* In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
* array of EnumerableSet.
* ====
*/
library EnumerableSet {
// To implement this library for multiple types with as little code
// repetition as possible, we write it in terms of a generic Set type with
// bytes32 values.
// The Set implementation uses private functions, and user-facing
// implementations (such as AddressSet) are just wrappers around the
// underlying Set.
// This means that we can only create new EnumerableSets for types that fit
// in bytes32.
struct Set {
// Storage of set values
bytes32[] _values;
// Position of the value in the `values` array, plus 1 because index 0
// means a value is not in the set.
mapping(bytes32 => uint256) _indexes;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function _add(Set storage set, bytes32 value) private returns (bool) {
if (!_contains(set, value)) {
set._values.push(value);
// The value is stored at length-1, but we add 1 to all indexes
// and use 0 as a sentinel value
set._indexes[value] = set._values.length;
return true;
} else {
return false;
}
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function _remove(Set storage set, bytes32 value) private returns (bool) {
// We read and store the value's index to prevent multiple reads from the same storage slot
uint256 valueIndex = set._indexes[value];
if (valueIndex != 0) {
// Equivalent to contains(set, value)
// To delete an element from the _values array in O(1), we swap the element to delete with the last one in
// the array, and then remove the last element (sometimes called as 'swap and pop').
// This modifies the order of the array, as noted in {at}.
uint256 toDeleteIndex = valueIndex - 1;
uint256 lastIndex = set._values.length - 1;
if (lastIndex != toDeleteIndex) {
bytes32 lastValue = set._values[lastIndex];
// Move the last value to the index where the value to delete is
set._values[toDeleteIndex] = lastValue;
// Update the index for the moved value
set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex
}
// Delete the slot where the moved value was stored
set._values.pop();
// Delete the index for the deleted slot
delete set._indexes[value];
return true;
} else {
return false;
}
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function _contains(Set storage set, bytes32 value) private view returns (bool) {
return set._indexes[value] != 0;
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function _length(Set storage set) private view returns (uint256) {
return set._values.length;
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function _at(Set storage set, uint256 index) private view returns (bytes32) {
return set._values[index];
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function _values(Set storage set) private view returns (bytes32[] memory) {
return set._values;
}
// Bytes32Set
struct Bytes32Set {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _add(set._inner, value);
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _remove(set._inner, value);
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
return _contains(set._inner, value);
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(Bytes32Set storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
return _at(set._inner, index);
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
bytes32[] memory store = _values(set._inner);
bytes32[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// AddressSet
struct AddressSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(AddressSet storage set, address value) internal returns (bool) {
return _add(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(AddressSet storage set, address value) internal returns (bool) {
return _remove(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(AddressSet storage set, address value) internal view returns (bool) {
return _contains(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(AddressSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(AddressSet storage set, uint256 index) internal view returns (address) {
return address(uint160(uint256(_at(set._inner, index))));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(AddressSet storage set) internal view returns (address[] memory) {
bytes32[] memory store = _values(set._inner);
address[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// UintSet
struct UintSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(UintSet storage set, uint256 value) internal returns (bool) {
return _add(set._inner, bytes32(value));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(UintSet storage set, uint256 value) internal returns (bool) {
return _remove(set._inner, bytes32(value));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(UintSet storage set, uint256 value) internal view returns (bool) {
return _contains(set._inner, bytes32(value));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(UintSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(UintSet storage set, uint256 index) internal view returns (uint256) {
return uint256(_at(set._inner, index));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(UintSet storage set) internal view returns (uint256[] memory) {
bytes32[] memory store = _values(set._inner);
uint256[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)
pragma solidity ^0.8.0;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
enum Rounding {
Down, // Toward negative infinity
Up, // Toward infinity
Zero // Toward zero
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds up instead
* of rounding down.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b - 1) / b can overflow on addition, so we distribute.
return a == 0 ? 0 : (a - 1) / b + 1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
* with further edits by Uniswap Labs also under MIT license.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2^256 + prod0.
uint256 prod0; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod0 := mul(x, y)
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
// Solidity will revert if denominator == 0, unlike the div opcode on its own.
// The surrounding unchecked block does not change this fact.
// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
require(denominator > prod1, "Math: mulDiv overflow");
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
// See https://cs.stackexchange.com/q/138556/92363.
// Does not overflow because the denominator cannot be zero at this stage in the function.
uint256 twos = denominator & (~denominator + 1);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 2^4.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
// in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // inverse mod 2^256
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
//
// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
//
// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
//
// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
uint256 result = 1 << (log2(a) >> 1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
// into the expected uint128 result.
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2, rounded down, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10, rounded down, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256, rounded down, of a positive value.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 256, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol)
pragma solidity ^0.8.0;
/**
* @dev Standard signed math utilities missing in the Solidity language.
*/
library SignedMath {
/**
* @dev Returns the largest of two signed numbers.
*/
function max(int256 a, int256 b) internal pure returns (int256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two signed numbers.
*/
function min(int256 a, int256 b) internal pure returns (int256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two signed numbers without overflow.
* The result is rounded towards zero.
*/
function average(int256 a, int256 b) internal pure returns (int256) {
// Formula from the book "Hacker's Delight"
int256 x = (a & b) + ((a ^ b) >> 1);
return x + (int256(uint256(x) >> 255) & (a ^ b));
}
/**
* @dev Returns the absolute unsigned value of a signed value.
*/
function abs(int256 n) internal pure returns (uint256) {
unchecked {
// must be unchecked in order to support `n = type(int256).min`
return uint256(n >= 0 ? n : -n);
}
}
}{
"remappings": [
"solmate/=lib/solmate/",
"permit2/=lib/permit2/",
"forge-std/=lib/forge-std/src/",
"@uniswap/v3-core/=node_modules/@uniswap/v3-core/",
"@uniswap/v2-core/=node_modules/@uniswap/v2-core/",
"@uniswap/v3-periphery/=lib/v3-periphery/",
"@uniswap/v4-periphery/=lib/uniswap-v4-periphery/",
"@hyperlane/=node_modules/@hyperlane-xyz/",
"@hyperlane-updated/=lib/hyperlane-monorepo/solidity/",
"@openzeppelin/contracts-upgradeable/=node_modules/@openzeppelin/contracts-upgradeable/",
"node_modules/@hyperlane-xyz/:@openzeppelin/contracts/=node_modules/@hyperlane-xyz/core/node_modules/@openzeppelin/contracts/",
"lib/hyperlane-monorepo/solidity/:@openzeppelin/contracts/=node_modules/@hyperlane-xyz/core/node_modules/@openzeppelin/contracts/",
"@openzeppelin/contracts/=lib/uniswap-v4-periphery/lib/uniswap-v4-core/lib/openzeppelin-contracts/contracts/",
"@ensdomains/=lib/uniswap-v4-periphery/lib/uniswap-v4-core/node_modules/@ensdomains/",
"@uniswap/v4-core/=lib/uniswap-v4-periphery/lib/uniswap-v4-core/",
"ds-test/=lib/solmate/lib/ds-test/src/",
"erc4626-tests/=lib/uniswap-v4-periphery/lib/uniswap-v4-core/lib/openzeppelin-contracts/lib/erc4626-tests/",
"forge-gas-snapshot/=lib/permit2/lib/forge-gas-snapshot/src/",
"fx-portal/=lib/hyperlane-monorepo/solidity/lib/fx-portal/contracts/",
"hardhat/=lib/uniswap-v4-periphery/lib/uniswap-v4-core/node_modules/hardhat/",
"hyperlane-monorepo/=lib/hyperlane-monorepo/",
"openzeppelin-contracts/=lib/permit2/lib/openzeppelin-contracts/",
"uniswap-v4-core/=lib/uniswap-v4-periphery/lib/uniswap-v4-core/src/",
"uniswap-v4-periphery/=lib/uniswap-v4-periphery/",
"v3-periphery/=lib/v3-periphery/contracts/"
],
"optimizer": {
"enabled": true,
"runs": 20000,
"details": {
"constantOptimizer": true,
"yul": true
}
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "ipfs",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "cancun",
"viaIR": true,
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"components":[{"internalType":"address","name":"permit2","type":"address"},{"internalType":"address","name":"weth9","type":"address"},{"internalType":"address","name":"v2Factory","type":"address"},{"internalType":"address","name":"v3Factory","type":"address"},{"internalType":"bytes32","name":"pairInitCodeHash","type":"bytes32"},{"internalType":"bytes32","name":"poolInitCodeHash","type":"bytes32"},{"internalType":"address","name":"v4PoolManager","type":"address"},{"internalType":"address","name":"veloV2Factory","type":"address"},{"internalType":"address","name":"veloCLFactory","type":"address"},{"internalType":"bytes32","name":"veloV2InitCodeHash","type":"bytes32"},{"internalType":"bytes32","name":"veloCLInitCodeHash","type":"bytes32"}],"internalType":"struct RouterDeployParameters","name":"params","type":"tuple"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"BalanceTooLow","type":"error"},{"inputs":[],"name":"ContractLocked","type":"error"},{"inputs":[{"internalType":"Currency","name":"currency","type":"address"}],"name":"DeltaNotNegative","type":"error"},{"inputs":[{"internalType":"Currency","name":"currency","type":"address"}],"name":"DeltaNotPositive","type":"error"},{"inputs":[],"name":"ETHNotAccepted","type":"error"},{"inputs":[{"internalType":"uint256","name":"commandIndex","type":"uint256"},{"internalType":"bytes","name":"message","type":"bytes"}],"name":"ExecutionFailed","type":"error"},{"inputs":[],"name":"FromAddressIsNotOwner","type":"error"},{"inputs":[],"name":"InputLengthMismatch","type":"error"},{"inputs":[],"name":"InsufficientBalance","type":"error"},{"inputs":[],"name":"InsufficientETH","type":"error"},{"inputs":[],"name":"InsufficientToken","type":"error"},{"inputs":[],"name":"InvalidBips","type":"error"},{"inputs":[{"internalType":"uint8","name":"bridgeType","type":"uint8"}],"name":"InvalidBridgeType","type":"error"},{"inputs":[{"internalType":"uint256","name":"commandType","type":"uint256"}],"name":"InvalidCommandType","type":"error"},{"inputs":[],"name":"InvalidEthSender","type":"error"},{"inputs":[],"name":"InvalidPath","type":"error"},{"inputs":[],"name":"InvalidRecipient","type":"error"},{"inputs":[],"name":"InvalidReserves","type":"error"},{"inputs":[],"name":"InvalidTokenAddress","type":"error"},{"inputs":[],"name":"LengthMismatch","type":"error"},{"inputs":[],"name":"NotPoolManager","type":"error"},{"inputs":[],"name":"SliceOutOfBounds","type":"error"},{"inputs":[],"name":"StableExactOutputUnsupported","type":"error"},{"inputs":[],"name":"TransactionDeadlinePassed","type":"error"},{"inputs":[],"name":"UnsafeCast","type":"error"},{"inputs":[{"internalType":"uint256","name":"action","type":"uint256"}],"name":"UnsupportedAction","type":"error"},{"inputs":[],"name":"V2InvalidPath","type":"error"},{"inputs":[],"name":"V2TooLittleReceived","type":"error"},{"inputs":[],"name":"V2TooMuchRequested","type":"error"},{"inputs":[],"name":"V3InvalidAmountOut","type":"error"},{"inputs":[],"name":"V3InvalidCaller","type":"error"},{"inputs":[],"name":"V3InvalidSwap","type":"error"},{"inputs":[],"name":"V3TooLittleReceived","type":"error"},{"inputs":[],"name":"V3TooMuchRequested","type":"error"},{"inputs":[{"internalType":"uint256","name":"minAmountOutReceived","type":"uint256"},{"internalType":"uint256","name":"amountReceived","type":"uint256"}],"name":"V4TooLittleReceived","type":"error"},{"inputs":[{"internalType":"uint256","name":"maxAmountInRequested","type":"uint256"},{"internalType":"uint256","name":"amountRequested","type":"uint256"}],"name":"V4TooMuchRequested","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"address","name":"localRouter","type":"address"},{"indexed":true,"internalType":"uint32","name":"destinationDomain","type":"uint32"},{"indexed":false,"internalType":"bytes32","name":"commitment","type":"bytes32"}],"name":"CrossChainSwap","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint32","name":"domain","type":"uint32"}],"name":"UniversalRouterBridge","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"}],"name":"UniversalRouterSwap","type":"event"},{"inputs":[],"name":"OPTIMISM_CHAIN_ID","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PERMIT2","outputs":[{"internalType":"contract IPermit2","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"UNISWAP_V2_FACTORY","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"UNISWAP_V2_PAIR_INIT_CODE_HASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"UNISWAP_V3_FACTORY","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"UNISWAP_V3_POOL_INIT_CODE_HASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"VELODROME_CL_FACTORY","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"VELODROME_CL_POOL_INIT_CODE_HASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"VELODROME_V2_FACTORY","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"VELODROME_V2_INIT_CODE_HASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"WETH9","outputs":[{"internalType":"contract IWETH9","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"commands","type":"bytes"},{"internalType":"bytes[]","name":"inputs","type":"bytes[]"}],"name":"execute","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes","name":"commands","type":"bytes"},{"internalType":"bytes[]","name":"inputs","type":"bytes[]"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"execute","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"msgSender","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"poolManager","outputs":[{"internalType":"contract IPoolManager","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"int256","name":"amount0Delta","type":"int256"},{"internalType":"int256","name":"amount1Delta","type":"int256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"uniswapV3SwapCallback","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"data","type":"bytes"}],"name":"unlockCallback","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]Contract Creation Code
6101e06040523461036a57604051601f61636538819003918201601f19168301916001600160401b03831184841017610356578084926101609460405283398101031261036a5760405161016081016001600160401b038111828210176103565760405261006c8261036e565b9182825261007c6020820161036e565b9182602082015261008f6040830161036e565b91604082019283526100a36060820161036e565b91606081019283526080820151936080820194855260a083015160a083019081526100d060c0850161036e565b958660c08501526100e360e0860161036e565b9160e085019283526100f8610100870161036e565b93610100860194855261014080610120890151986101208901998a52015196019586526040519a60408c018c811060018060401b038211176103565760409081526001600160a01b039182168d529a811660208d01908152915198519351925194519551975196519a51919a9897811697958116959381169316906101008101906001600160401b038211818310176103565760409182528281526020810194855280820193845260608101958652608080820197885260a08083019a8b5260c08084019a8b5260e09384019c8d5294909152935190935292516001600160a01b03908116909152925190529151811661010052915161012052915181166101405291516101605291811661018052915182166101a0529151166101c05251615fe2908161038382396080518181816105f90152818161187e01526147e6015260a051818181610757015281816118a3015261480e015260c051818181610477015261394a015260e051818181610574015261397001526101005181818161081f01528181611b910152818161223a015281816142c401526148fa01526101205181818161053a01528181611bb6015261491f0152610140518181816105b601526139c301526101605181818161099001526139e90152610180518181816087015281816104ba0152818161065801528181612b5501528181612ceb01528181615548015281816155bf015281816156ad0152818161580b0152615dda01526101a0518181816025015281816107dc0152818161256101526126e001526101c05181818161079901528181611414015281816128c30152613e300152f35b634e487b7160e01b5f52604160045260245ffd5b5f80fd5b51906001600160a01b038216820361036a5756fe6102808060405260043610156100af575b50361561001b575f80fd5b6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000163314158061007c575b61005457005b7f38bbd576000000000000000000000000000000000000000000000000000000005f5260045ffd5b506001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001633141561004e565b5f3560e01c9081630f54cdb614610a625750806324856bc3146109b35780633452c266146109795780633593564c14610843578063407bbdb2146108005780634aa4a4fc146107bd5780636afdd8501461077a57806379818a581461074057806391dd73461461061d57806399d8fae3146105da5780639f65a7c414610597578063aad8a4911461055d578063b7cc989c14610523578063d737d0c7146104de578063dc4c90d31461049b578063f73e5aab146104585763fa461e3314610176575f610010565b3461032c57606060031936011261032c5760243560043560443567ffffffffffffffff811161032c576101ad903690600401610a7b565b925f83139384158061044e575b610426578083019060608483031261032c5783359167ffffffffffffffff831161032c5782850181601f8201121561032c5780356101f7816137b5565b926102056040519485613792565b8184526020828401011161032c575f92816020809401848301370101526020840135926001600160a01b03841680940361032c57604085013592831515840361032c5763ffffffff1685019463ffffffff86351692602087019160208886010191011061041957601791602b84106103f15781359861029a8662ffffff60378d60601c9b013560601c9c60481c168c8b613926565b6001600160a01b03339116036103c957156103bf57508786105b156102cb57505050506102c993503391613a3b565b005b9193945091949582602b0180602b11610392578410610330575082821161032c5781019103907f800000000000000000000000000000000000000000000000000000000000000085101561032c576102c99461032733916137d1565b613b74565b5f80fd5b939594505050507faf28d9864a81dfdf71cab65f4e5d79a0cf9b083905fb8971425e6cb581b3f6925c821161036a576102c9923391613a3b565b7f739dbe52000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b96508588106102b4565b7f32b13d91000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f3b99b53d000000000000000000000000000000000000000000000000000000005f5260045ffd5b633b99b53d5f526004601cfd5b7f316cf0eb000000000000000000000000000000000000000000000000000000005f5260045ffd5b505f8213156101ba565b3461032c575f60031936011261032c5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b3461032c575f60031936011261032c5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b3461032c575f60031936011261032c5760207f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c6001600160a01b0360405191168152f35b3461032c575f60031936011261032c5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b3461032c575f60031936011261032c5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b3461032c575f60031936011261032c5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b3461032c575f60031936011261032c5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b3461032c57602060031936011261032c5760043567ffffffffffffffff811161032c5761064e903690600401610a7b565b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001633036107185761068791613ec6565b908183036106f0575f5b8381106106c4576106c06040516106a9602082613792565b5f8152604051918291602083526020830190610ada565b0390f35b806106ea6106d56001938789610aff565b3560f81c6106e4838787610b89565b91614abc565b01610691565b7faaad13f7000000000000000000000000000000000000000000000000000000005f5260045ffd5b7fae18210a000000000000000000000000000000000000000000000000000000005f5260045ffd5b3461032c575f60031936011261032c5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b3461032c575f60031936011261032c5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b3461032c575f60031936011261032c5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b3461032c575f60031936011261032c5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b606060031936011261032c5760043567ffffffffffffffff811161032c5761086f903690600401610a7b565b60243567ffffffffffffffff811161032c5761088f903690600401610aa9565b91604435421161095157333014610948576001600160a01b037f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c16610920576108fb93337f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085d610ba4565b5f7f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085d005b7f6f5ffb7e000000000000000000000000000000000000000000000000000000005f5260045ffd5b6102c993610ba4565b7f5bf6f916000000000000000000000000000000000000000000000000000000005f5260045ffd5b3461032c575f60031936011261032c5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b604060031936011261032c5760043567ffffffffffffffff811161032c576109df903690600401610a7b565b60243567ffffffffffffffff811161032c576109ff903690600401610aa9565b91333014610948576001600160a01b037f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c16610920576108fb93337f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085d610ba4565b3461032c575f60031936011261032c5780600a60209252f35b9181601f8401121561032c5782359167ffffffffffffffff831161032c576020838186019501011161032c57565b9181601f8401121561032c5782359167ffffffffffffffff831161032c576020808501948460051b01011161032c57565b90601f19601f602080948051918291828752018686015e5f8582860101520116010190565b90821015610b0b570190565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18136030182121561032c570180359067ffffffffffffffff821161032c5760200191813603831361032c57565b90821015610b0b57610ba09160051b810190610b38565b9091565b90808403613721579392915f610160525b846101605110610bc6575050509050565b610bd4610160518684610aff565b3593610be4610160518584610b89565b6101c0919091526060610180526001949060f887901c603f1660218110156135c8576010811015612af25760088110156117c957806110815750610c2b906101c0516138fb565b93909460806101c05101355f1461107a577f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c955b610c6d6101c05135613d00565b969086816102605260206101c0510135917f800000000000000000000000000000000000000000000000000000000000000060206101c051013514610fe8575b50505b602b6042881015610240527f800000000000000000000000000000000000000000000000000000000000000082101561032c576102405115610fe15730915b88821161032c576040916001600160a01b035f610260513595610de5610d6d610d9085610d418b60601c9b601761026051013560601c9c8d81109d62ffffff60a06101c05101359360481c1691613926565b16968a8614610fc6576401000276a49b5b878b51948593606060208601526080850190610260516137fd565b91168b83015260a06101c05101351515606083015203601f198101835282613792565b8488519a8b98899788967f128acb080000000000000000000000000000000000000000000000000000000088521660048701528b6024870152604486015216606484015260a0608484015260a4830190610ada565b03925af1908115610fbb575f905f92610f7f575b610e09935015610f7857506137d1565b94610240515f14610e505730908060171161032c57610260805160170190527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe90195610cb0565b50949095919692979360406101c051013511610f50576001600160a01b03807f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c169116907faa2dd386ed08047486db39ea1a9e5461b6a95c8bb3ca845cce27a537799a46725f80a35b159081610f25575b50610eda576001610160510161016052939291610bb5565b6040517f2c4029e90000000000000000000000000000000000000000000000000000000081526101605160048201526040602482015280610f216044820161018051610ada565b0390fd5b7f8000000000000000000000000000000000000000000000000000000000000000915016155f610ec2565b7f39d35496000000000000000000000000000000000000000000000000000000005f5260045ffd5b90506137d1565b9150506040823d8211610fb3575b81610f9a60409383613792565b8101031261032c57816020610e09935191015191610df9565b3d9150610f8d565b6040513d5f823e3d90fd5b73fffd8963efd1fc6a506488495d951d5263988d259b610d52565b8891610cef565b602492506110006020926001600160a01b03926151e7565b16604051928380927f70a082310000000000000000000000000000000000000000000000000000000082523060048301525afa908115610fbb575f91611049575b505f80610cad565b90506020813d8211611072575b8161106360209383613792565b8101031261032c57515f611041565b3d9150611056565b3095610c60565b6001819995979293989499145f146112265750906110aa60206101c0510135916101c0516138fb565b6101c051919291608001351561121f577f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c925b6110eb6101c05135613d00565b9360406101c05101357faf28d9864a81dfdf71cab65f4e5d79a0cf9b083905fb8971425e6cb581b3f6925d7f800000000000000000000000000000000000000000000000000000000000000084101561032c576111559260a06101c05101359286610327876137d1565b909190156112105750611167906137d1565b036111e8575f7faf28d9864a81dfdf71cab65f4e5d79a0cf9b083905fb8971425e6cb581b3f6925d6001600160a01b03807f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c169116907faa2dd386ed08047486db39ea1a9e5461b6a95c8bb3ca845cce27a537799a46725f80a35b610eba565b7fd4e0248e000000000000000000000000000000000000000000000000000000005f5260045ffd5b61121a91506137d1565b611167565b30926110de565b6002810361127a575090506111e37f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c60406101c05101359061126f60206101c0510135613d00565b906101c05135613e24565b600381036114655750506101c051356101c0510163ffffffff60206101c0510135166101c051019063ffffffff82351692602080840193850101906101c0510110610419577f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c92604051926001600160a01b0360208501957f2a2d80d10000000000000000000000000000000000000000000000000000000087521660248501526060604485015260e484019280357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18236030181121561032c5781016020813591019467ffffffffffffffff821161032c578160071b3603861361032c5781906060608489015252610104860194905f905b80821061144757505050936113f95f96948294611407946040896001600160a01b036113bd60208e9d0161385b565b1660a4880152013560c48601527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc8584030160648601526137fd565b03601f198101835282613792565b5190826001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165af161143e61381d565b61018052610eba565b9091956080808261145a6001948b6138a0565b01970192019061138e565b909150600481036115bc575061148160206101c0510135613d00565b6001600160a01b0360406101c0510135166001600160a01b036101c051351680155f146114f65750479081106114ce57806114be575b5050610eba565b6114c791614093565b5f806114b7565b7f6a12f104000000000000000000000000000000000000000000000000000000005f5260045ffd5b91604051917f70a08231000000000000000000000000000000000000000000000000000000008352306004840152602083602481875afa928315610fbb575f93611589575b5082106115615781611550575b505050610eba565b61155992614a04565b5f8080611548565b7f675cae38000000000000000000000000000000000000000000000000000000005f5260045ffd5b9092506020813d82116115b4575b816115a460209383613792565b8101031261032c5751915f61153b565b3d9150611597565b600581036115ea57506111e360406101c05101356115e060206101c0510135613d00565b6101c05135613d55565b600681036116bd575060406101c051013561160b60206101c0510135613d00565b6101c051356001600160a01b031680611632575061162c6111e392476149c2565b90614093565b906040517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152602081602481865afa908115610fbb575f9161168a575b506111e393611684916149c2565b91614a04565b90506020813d82116116b5575b816116a460209383613792565b8101031261032c57516111e3611676565b3d9150611697565b6007036111e3576101c0513560406101c05101357f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c7f80000000000000000000000000000000000000000000000000000000000000008214611736575b916111e39261173060206101c0510135613d00565b91613a3b565b9050604051917f70a082310000000000000000000000000000000000000000000000000000000083526001600160a01b03821660048401526020836024816001600160a01b0385165afa8015610fbb575f90611796575b9092509061171b565b506020833d82116117c1575b816117af60209383613792565b8101031261032c576111e3925161178d565b3d91506117a2565b60088199959793989499145f14611c56575060206101c05101356117f860a06101c0510135926101c0516138fb565b919060806101c05101355f14611c4f577f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c905b6118396101c05135613d00565b928185017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec013560601c928615611b7d57602886811161032c576001600160a01b03907f0000000000000000000000000000000000000000000000000000000000000000906119937f0000000000000000000000000000000000000000000000000000000000000000916118d8601489013560601c893560601c6151f6565b90917fffffffffffffffffffffffffffffffffffffffff0000000000000000000000006040519281602085019560601b16855260601b1660348301528152611921604882613792565b5190206113f9604051938492602084019687917fffffffffffffffffffffffffffffffffffffffff000000000000000000000000605594927fff00000000000000000000000000000000000000000000000000000000000000855260601b166001840152601583015260358201520190565b51902016915b8281611b62575b505050604051937f70a082310000000000000000000000000000000000000000000000000000000085526001600160a01b03811696876004870152602086602481885afa958615610fbb575f96611b22575b506024966020959493929115611b1457611a0b936144dc565b604051938480927f70a082310000000000000000000000000000000000000000000000000000000082528760048301525afa918215610fbb575f92611adf575b50611a5e9060406101c051013592613d48565b10611ab7576001600160a01b037f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c167faa2dd386ed08047486db39ea1a9e5461b6a95c8bb3ca845cce27a537799a46725f80a3610eba565b7f849eaf98000000000000000000000000000000000000000000000000000000005f5260045ffd5b9091506020813d8211611b0c575b81611afa60209383613792565b8101031261032c575190611a5e611a4b565b3d9150611aed565b611b1d93614147565b611a0b565b949392919095506020853d8211611b5a575b81611b4160209383613792565b8101031261032c579351949293919290919060246119f2565b3d9150611b34565b611b7592611b7089876151e7565b613a3b565b5f80826119a0565b602986811161032c576001600160a01b03907f000000000000000000000000000000000000000000000000000000000000000090611c457f000000000000000000000000000000000000000000000000000000000000000091611beb601589013560601c893560601c6151f6565b90917fffffffffffffffffffffffffffffffffffffffff0000000000000000000000006040519281602085019560601b16855260601b16603483015260148a01355f1a151560f81b60488301528152611921604982613792565b5190201691611999565b309061182c565b92979196939594939192600981036124035750611c76906101c0516138fb565b610200529360806101c05101355f146123fc577f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c935b611cba6101c05135613d00565b966102005161022052866080525f6101e0525f60c0525f60c05260a06101c05101355f146123f057602861020051105b6123c8576101c05160208101356101e05260a0013515612116575b60286102205110612025577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd8610220510161022051811161039257611d5590610220519061022051608051613b5c565b6101a052611d686101a0518260016154bf565b90610120526101205150604051907f0902f1ac0000000000000000000000000000000000000000000000000000000082526060826004816001600160a01b0361012051165afa928315610fbb575f925f94611fe1575b506001600160a01b03611dd681926101a051906151e7565b925f61010052169116145f14611fd757610100525b610100516101205160c0525f9190158015611fcf575b611fa7576101c05160a0013515611f2b57611e226101e051610100516147c2565b906103e88202918083046103e81490151715611efe576101e051611e4591613d48565b906103e58202918083046103e51490151715611efe5790611e65916149b8565b9060018201809211611ed157505b6101e052610220517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec8101116103925761022080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec019052611d05565b807f4e487b7100000000000000000000000000000000000000000000000000000000602492526011600452fd5b6024837f4e487b710000000000000000000000000000000000000000000000000000000081526011600452fd5b611f549150611f4e611f43610100516101e0516147c2565b916101e05190613d48565b906149b8565b6127108102908082046127101490151715610392576126f290046001810180911115611e73577f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b7f7b9c8916000000000000000000000000000000000000000000000000000000005f5260045ffd5b508015611e01565b9061010052611deb565b6001600160a01b03919450611dd69350612012829160603d811161201e575b61200a8183613792565b810190614100565b50959094509150611dbe565b503d612000565b949893989792979691969590955b60406101c05101356101e051116120ee5761205e906101e0519060c05190611b7061020051866151e7565b6101c05160a00135156120d65761207e908260c0519161020051906144dc565b6001600160a01b03807f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c169116907faa2dd386ed08047486db39ea1a9e5461b6a95c8bb3ca845cce27a537799a46725f80a3610eba565b6120e9908260c051916102005190614147565b61207e565b7f8ab0bc16000000000000000000000000000000000000000000000000000000005f5260045ffd5b602961022051106123b657602961022051036102205181116103925761214790610220519061022051608051613b5c565b906101405260146101405101355f1a60e05261216781610140515f6154bf565b906040517f0902f1ac0000000000000000000000000000000000000000000000000000000081526060816004816001600160a01b0386165afa928315610fbb5761222e945f925f95612382575b506001600160a01b036121cb8192610140516151e7565b92169116145f146123795760209092915b60c08190526040517fcc56b2c500000000000000000000000000000000000000000000000000000000815260e0516001600160a01b039092166004820152901515602482015293849081906044820190565b03816001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165afa928315610fbb575f93612346575b508115801561233e575b611fa75760e05161231657611f4e611f43612292936101e0516147c2565b90612710820291808304612710149015171561039257612710036127108111610392576122be916149b8565b60018101809111610392576101e052610220517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeb810160a081905211610392576102205160a0511161032c5760a05161022052612116565b7fbd534ea5000000000000000000000000000000000000000000000000000000005f5260045ffd5b508015612274565b9092506020813d8211612371575b8161236160209383613792565b8101031261032c5751915f61226a565b3d9150612354565b906020906121dc565b6001600160a01b039195506121cb93506123aa829160603d811161201e5761200a8183613792565b509690945091506121b4565b94989398979297969196959095612033565b7f20db8267000000000000000000000000000000000000000000000000000000005f5260045ffd5b60296102005110611cea565b3093611cad565b92979196949593949192600a81036124ff5750905063ffffffff60c06101c0510135166101c0510163ffffffff81351691602080830192840101906101c0510110610419575f9182916114077f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c926113f96040519384926001600160a01b0360208501977f2b67b5700000000000000000000000000000000000000000000000000000000089521660248501526124c1604485016101c0516138a0565b6001600160a01b036124d860806101c0510161385b565b1660c485015260a06101c051013560e48501526101006101048501526101248401916137fd565b600b810361269b57505060206101c051013561251e6101c05135613d00565b90807f8000000000000000000000000000000000000000000000000000000000000000810361266c575050475b80612557575050610eba565b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001691823b1561032c57604051907fd0e30db00000000000000000000000000000000000000000000000000000000082525f8260048186885af1918215610fbb576001600160a01b039261265c575b5016903082036125df575b506114b7565b60446020925f60405195869485937fa9059cbb000000000000000000000000000000000000000000000000000000008552600485015260248401525af18015610fbb5761262e575b80806125d9565b61264e9060203d8111612655575b6126468183613792565b810190613a23565b505f612627565b503d61263c565b5f61266691613792565b5f6125ce565b47101561254b577f6a12f104000000000000000000000000000000000000000000000000000000005f5260045ffd5b600c81036127ef5750506126b26101c05135613d00565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316602082602481845afa918215610fbb575f926127bc575b5060206101c051013582106114ce578161273b57505050610eba565b803b1561032c575f80916024604051809481937f2e1a7d4d0000000000000000000000000000000000000000000000000000000083528760048401525af18015610fbb576127ac575b50306001600160a01b0383160361279c575b80611548565b6127a591614093565b5f80612796565b5f6127b691613792565b5f612784565b9091506020813d82116127e7575b816127d760209383613792565b8101031261032c5751905f61271f565b3d91506127ca565b600d81999395989299969496145f146129dd57506101c051356101c051019586359861282360206101c0518a03018b613a16565b116103f1576001600160a01b037f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c1697935f5b8a8110156128af57896001600160a01b0361287960208460071b8d0101613cec565b160361288757600101612857565b7fe7002877000000000000000000000000000000000000000000000000000000005f5260045ffd5b509593949298919750956001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690813b1561032c57916040519283917f0d58b1db000000000000000000000000000000000000000000000000000000008352806024840160206004860152526020604484019201905f905b8082106129635750505091815f81819503925af18015610fbb57612953575b50610eba565b5f61295d91613792565b5f61294d565b919350916080806001926001600160a01b0361297e8861385b565b1681526001600160a01b036129956020890161385b565b1660208201526001600160a01b036129af6040890161385b565b1660408201526001600160a01b036129c96060890161385b565b16606082015201940192018593929161292e565b919750959193919250600e8103612ac757506040517f70a082310000000000000000000000000000000000000000000000000000000081526001600160a01b036101c051351660048201526020816024816001600160a01b03846101c0510135165afa908115610fbb575f91612a96575b506101c051604001351115806111e3576040517fa32816720000000000000000000000000000000000000000000000000000000060208201526004815261143e602482613792565b90506020813d8211612abf575b81612ab060209383613792565b8101031261032c57515f612a4e565b3d9150612aa3565b7fd76a1e9e000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b60108199959793989499145f14612bfd57505f612b4891604051809381927f48c894910000000000000000000000000000000000000000000000000000000083526020600484015260248301906101c0516137fd565b0381836001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165af18015610fbb57612b875750610eba565b3d805f833e612b968183613792565b81019060208183031261032c5780519067ffffffffffffffff821161032c570181601f8201121561032c578051612bcc816137b5565b92612bda6040519485613792565b8184526020828401011161032c575f928160208094018483015e0101525f61294d565b60118103612d15575050505f8060405160208101907f6276cbbe0000000000000000000000000000000000000000000000000000000082526001600160a01b03612c496101c05161385b565b1660248201526001600160a01b03612c6660206101c0510161385b565b16604482015262ffffff612c7f60406101c0510161386f565b166064820152612c9460606101c0510161387f565b60020b60848201526001600160a01b03612cb360806101c0510161385b565b1660a48201526001600160a01b0360a06101c05101351660c482015260c48152612cde60e482613792565b5190826001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165af161143e61381d565b6012810361341c5750506101c0517f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c9060408101359060808101359060c08101359060608101359060208101359060a08101359060e00135156134155786915b6001600160a01b0381166001036134105750865b7f80000000000000000000000000000000000000000000000000000000000000008614613383575b6001600160a01b031695861561335b576101c0513560ff166001810361314657506001600160a01b03841691604051937f996c6cc3000000000000000000000000000000000000000000000000000000008552602085600481875afa8015610fbb575f906130fc575b6001600160a01b038481169650168590036130d45787612e3c918785613f5e565b604051907f775313a100000000000000000000000000000000000000000000000000000000825263ffffffff871690816004840152602083602481885afa948515610fbb578b935f9661309e575b50612f23607660209788604051612ea18282613792565b5f81527fffffffffffffffffffffffffffffffffffffffff000000000000000000000000604051998a947e01000000000000000000000000000000000000000000000000000000000000858701525f6022870152604286015260601b1660628401528051918291018484015e81015f838201520301601f198101865285613792565b6040517f7f5a7c7b0000000000000000000000000000000000000000000000000000000081528681600481855afa928315610fbb5787948c948e935f91613058575b506001600160a01b03612fbe604051998a98899788967f51debffc00000000000000000000000000000000000000000000000000000000885260048801526024870152604486015260a0606486015260a4850190610ada565b9116608483015203925af1958615610fbb576130156040966001600160a01b0396879663ffffffff957f7bdf8294c8b336e56cb4381cf529bf217d329da7a3b80e3705bf4da8ba7aa8569b61302a575b5050614039565b855197885216602087015216951692a4610eba565b8161304992903d10613051575b6130418183613792565b81019061384c565b505f8061300e565b503d613037565b93505093509381813d8311613097575b6130728183613792565b8101031261032c57516001600160a01b038116810361032c5786938b938d925f612f65565b503d613068565b935094506020833d82116130cc575b816130ba60209383613792565b8101031261032c578a9251945f612e8a565b3d91506130ad565b7f1eb00b06000000000000000000000000000000000000000000000000000000005f5260045ffd5b50936020813d821161313e575b8161311660209383613792565b8101031261032c5751936001600160a01b038516850361032c576001600160a01b0394612e1b565b3d9150613109565b9193929160028103613330575046600a036132bd57604051917f785e9e860000000000000000000000000000000000000000000000000000000083526020836004816001600160a01b0388165afa8015610fbb576001600160a01b03935f9161328f575b505b83831693849116036130d457866131c4918584613f5e565b6001600160a01b03831693843b1561032c57886084895f936001600160a01b0398604051998a9586947fdbf2e4b700000000000000000000000000000000000000000000000000000000865260048601528d602486015263ffffffff8d1660448601521660648401525af1938415610fbb577f7bdf8294c8b336e56cb4381cf529bf217d329da7a3b80e3705bf4da8ba7aa8569561327a6001600160a01b0395869563ffffffff9460409961327f575b50614039565b613015565b5f61328991613792565b5f613274565b6132b0915060203d81116132b6575b6132a88183613792565b810190613ccd565b5f6131aa565b503d61329e565b604051917fffcbd8d00000000000000000000000000000000000000000000000000000000083526020836004816001600160a01b0388165afa8015610fbb576001600160a01b03935f91613312575b506131ac565b61332a915060203d81116132b6576132a88183613792565b5f61330c565b7f6a85a5d8000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b7f9c8d2cd2000000000000000000000000000000000000000000000000000000005f5260045ffd5b94506040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526020816024816001600160a01b038b165afa8015610fbb575f906133d8575b959050612db2565b506020813d8211613408575b816133f160209383613792565b8101031261032c576001600160a01b0390516133d0565b3d91506133e4565b612d8a565b3091612d76565b60138103612ac7575060806101c051013563ffffffff60e06101c0510135166101c051019063ffffffff82351692602080840193850101906101c0510110610419576001600160a01b0360206101c0510135169160406135076001600160a01b037f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c1692825193849283927fa53e921000000000000000000000000000000000000000000000000000000000845263ffffffff6101c0513516998a6004860152866101c0510135602486015260606101c0510135604486015260e0606486015260e48501916137fd565b906001600160a01b0360c06101c051013516608484015260a48301528560c4830152038160a06101c0510135875af18015610fbb5761359d575b507f8907eccad16a44af80785607b6783b3878f6973673e4d0e86bc915d0bf9bd9c860206001600160a01b037f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c1692604051908152a4610eba565b604090813d81116135c1575b6135b38183613792565b8101031261032c575f613541565b503d6135a9565b60218199959793989499145f14612ac757506135e7906101c051613ec6565b61362a6040959395519460208601967f24856bc30000000000000000000000000000000000000000000000000000000088526040602488015260648701916137fd565b927fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc858503016044860152818452602084019160208160051b86010194845f907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1813603015b8483106136c557505050505050505091816136b75f9493859403601f198101835282613792565b519082305af161143e61381d565b9091929394959697601f1985820301885288358281121561032c578301906020823592019167ffffffffffffffff811161032c57803603831361032c5761371160209283928b956137fd565b9a01980196959493019190613690565b7fff633a38000000000000000000000000000000000000000000000000000000005f5260045ffd5b60a0810190811067ffffffffffffffff82111761376557604052565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b90601f601f19910116810190811067ffffffffffffffff82111761376557604052565b67ffffffffffffffff811161376557601f01601f191660200190565b7f80000000000000000000000000000000000000000000000000000000000000008114610392575f0390565b601f8260209493601f1993818652868601375f8582860101520116010190565b3d15613847573d9061382e826137b5565b9161383c6040519384613792565b82523d5f602084013e565b606090565b9081602091031261032c575190565b35906001600160a01b038216820361032c57565b359062ffffff8216820361032c57565b35908160020b820361032c57565b359065ffffffffffff8216820361032c57565b65ffffffffffff6138f5606080936001600160a01b036138bf8261385b565b1686526001600160a01b036138d66020830161385b565b166020870152836138e96040830161388d565b1660408701520161388d565b16910152565b909163ffffffff60608301351682019263ffffffff8435169260208086019585010191011061041957565b6001600160a01b039392919284841685831611613a0e575b6139ba9190156139c1577f00000000000000000000000000000000000000000000000000000000000000009362ffffff7f0000000000000000000000000000000000000000000000000000000000000000945b876040519381602086019616865216604084015216606082015260608152611921608082613792565b5190201690565b7f00000000000000000000000000000000000000000000000000000000000000009362ffffff7f000000000000000000000000000000000000000000000000000000000000000094613991565b90929061393e565b9190820180921161039257565b9081602091031261032c5751801515810361032c5790565b9291906001600160a01b038116308103613a5c575050613a5a92613d55565b565b5f809160409695965160208101917f23b872dd00000000000000000000000000000000000000000000000000000000835260248201526001600160a01b038616604482015287606482015260648152613ab6608482613792565b519082875af1613ac461381d565b9015908115613b2c575b50613ada575b50505050565b6001600160a01b038411613b04576001600160a01b03613afb941692613e24565b5f808080613ad4565b7fc4bd89a9000000000000000000000000000000000000000000000000000000005f5260045ffd5b8051801515925082613b41575b50505f613ace565b613b549250602080918301019101613a23565b155f80613b39565b9093929384831161032c57841161032c578101920390565b949390919293602b85106103f1578335948560601c93601786013560601c948381871096879960481c62ffffff1690613bac93613926565b6001600160a01b031693855f14966001600160a01b038095613c115f96613c649560409c613cb2576401000276a4935b85613bf68f519889956060602088015260808701916137fd565b92168e8401521515606083015203601f198101855284613792565b89519b8c998a9889977f128acb080000000000000000000000000000000000000000000000000000000089521660048801526024870152604486015216606484015260a0608484015260a4830190610ada565b03925af18015610fbb575f925f91613c7b57509192565b9250506040823d604011613caa575b81613c9760409383613792565b8101031261032c57602082519201519192565b3d9150613c8a565b73fffd8963efd1fc6a506488495d951d5263988d2593613bdc565b9081602091031261032c57516001600160a01b038116810361032c5790565b356001600160a01b038116810361032c5790565b6001600160a01b03811660018103613d395750507f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c90565b600203613d4557503090565b90565b9190820391821161039257565b9091906001600160a01b031680613d705750613a5a91614093565b7f80000000000000000000000000000000000000000000000000000000000000008214613da2575b91613a5a92614a04565b9050604051917f70a08231000000000000000000000000000000000000000000000000000000008352306004840152602083602481855afa8015610fbb575f90613df0575b90925090613d98565b506020833d602011613e1c575b81613e0a60209383613792565b8101031261032c57613a5a9251613de7565b3d9150613dfd565b91926001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001691823b1561032c575f6001600160a01b039384829681608496816040519b8c9a8b997f36c78516000000000000000000000000000000000000000000000000000000008b521660048a01521660248801521660448601521660648401525af18015610fbb57613ebc5750565b5f613a5a91613792565b604081351891606082019363ffffffff6040840135169363ffffffe0601f8601169060608201602086013518179084019260608401359463ffffffff861694641fffffffe0608082019760051b1680915f925b808410613f3157506080925001019101101761041957565b90916020809163ffffffe0601f60808089890101359b848d18179b88010135011601019301929190613f19565b905f6001600160a01b036044936020968396813085831603614026575b505082604051957f095ea7b30000000000000000000000000000000000000000000000000000000087521660048601526024850152165af13d15601f3d1160015f511416171615613fc857565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f415050524f56455f4641494c45440000000000000000000000000000000000006044820152fd5b61403291309085613a3b565b5f81613f7b565b60446020925f80936001600160a01b03604051937f095ea7b30000000000000000000000000000000000000000000000000000000085521660048401528160248401525af13d15601f3d1160015f511416171615613fc857565b5f80809381935af1156140a257565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f4554485f5452414e534645525f4641494c4544000000000000000000000000006044820152fd5b9081606091031261032c578051916040602083015192015190565b90613d4594936080936001600160a01b0392845260208401521660408201528160608201520190610ada565b91927fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec820191808311610392576015928390049182156144875760298083811161032c575091959490916141a785830135606090811c908435901c6151f6565b50965f926020985b8685106141c25750505050505050505050565b87850285810489148615171561039257848101808211610392576141e7918585613b5c565b506040517f0902f1ac0000000000000000000000000000000000000000000000000000000081526060816004816001600160a01b038c165afa908115610fbb575f905f92614465575b50823560601c6001600160a01b0385160361446057905b604051907f70a082310000000000000000000000000000000000000000000000000000000082526001600160a01b038a1660048301528d82602481873560601c5afa908115610fbb578c925f92614427575b50926142e892916001600160a01b039460148701355f1a9387013560601c928d81893560601c9403907f000000000000000000000000000000000000000000000000000000000000000061523e565b9216903560601c145f14614420575f5b8a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff890187101561441757506001860191898302928084048b1490151715610392578583019182841161039257614355614376938e958888613b5c565b90939061436e8d860135606090811c908735901c6151f6565b50945f6147d5565b925b986143866040519586613792565b5f8552601f198e01368f8701376001600160a01b0381163b1561032c575f92836001600160a01b0386946143e9604051998a97889687947f022c0d9f0000000000000000000000000000000000000000000000000000000086526004860161411b565b0393165af1918215610fbb57600192614407575b50940193946141af565b5f61441191613792565b5f6143fd565b918a915f614378565b5f906142f8565b8f809294508193503d8311614459575b6144418183613792565b8101031261032c57518b916001600160a01b03614299565b503d614437565b614247565b905061447f915060603d811161201e5761200a8183613792565b50905f614230565b7fae52ad0c000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b9290919260288310614487576144fe813560601c601483013560601c906151f6565b509160148404937ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff86019501925f91602097601f198901965b88851061456b5750505050505050505050565b6001600160a01b0361457e868587615500565b503560601c961695604051907f0902f1ac0000000000000000000000000000000000000000000000000000000082526060826004818b5afa928315610fbb578c905f935f95614796575b506001600160a01b0316821492831561478c579060249194925b604051928380927f70a082310000000000000000000000000000000000000000000000000000000082528d60048301525afa908115610fbb575f9161475f575b5083810391841591828015614757575b611fa757856103e585029485046103e514911417156103925761465590836147c2565b906103e885029485046103e814171561039257611f4e614676928e95613a16565b9015614751575f905b88871015614749576146bd91614699600189018789615500565b5f949194506146b4853560601c601487013560601c906151f6565b509460016147d5565b925b988b604051956146cf8188613792565b5f87523690870137803b1561032c575f9283859361471c604051988996879586947f022c0d9f0000000000000000000000000000000000000000000000000000000086526004860161411b565b03925af1918215610fbb57600192614739575b5094019394614558565b5f61474391613792565b5f61472f565b83915f6146bf565b5f61467f565b508115614632565b90508c81813d8311614785575b6147768183613792565b8101031261032c57515f614622565b503d61476c565b91602491906145e2565b6147b99195506001600160a01b03945060603d811161201e5761200a8183613792565b509490936145c8565b8181029291811591840414171561039257565b6001600160a01b039250156148f8577f0000000000000000000000000000000000000000000000000000000000000000906139ba6148407f000000000000000000000000000000000000000000000000000000000000000092601481013560601c903560601c6151f6565b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000006040519181602084019460601b16845260601b16603482015260288152611921604882613792565b6113f9604051938492602084019687917fffffffffffffffffffffffffffffffffffffffff000000000000000000000000605594927fff00000000000000000000000000000000000000000000000000000000000000855260601b166001840152601583015260358201520190565b7f0000000000000000000000000000000000000000000000000000000000000000906139ba7f0000000000000000000000000000000000000000000000000000000000000000916014614956601583013560601c833560601c6151f6565b9190927fffffffffffffffffffffffffffffffffffffffff0000000000000000000000006040519381602086019660601b16865260601b16603484015201355f1a151560f81b6048820152602981526149b0604982613792565b519020614889565b81156144af570490565b61271082116149dc57612710916149d8916147c2565b0490565b7fdeaa01e6000000000000000000000000000000000000000000000000000000005f5260045ffd5b5f91826044926020956001600160a01b03604051947fa9059cbb00000000000000000000000000000000000000000000000000000000865216600485015260248401525af13d15601f3d1160015f511416171615614a5e57565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f5452414e534645525f4641494c454400000000000000000000000000000000006044820152fd5b929190600b8410156150155760078403614c2e57614adb929350615918565b6020810190614aea8282615926565b90505f92614af783613cec565b90614b0460408501615885565b906fffffffffffffffffffffffffffffffff821615614c15575b92915f915b838310614ba45750505050506060016fffffffffffffffffffffffffffffffff80614b4d83615885565b169216918210614b5b575050565b614b756fffffffffffffffffffffffffffffffff91615885565b7f8b063d73000000000000000000000000000000000000000000000000000000005f521660045260245260445ffd5b8496506fffffffffffffffffffffffffffffffff614bf991614be0614bd986614bd3614bfe9798999a8c615926565b9061597a565b9586615e6d565b614bed6080880188610b38565b949093165f0391615ce7565b615f46565b946001614c0b8793613cec565b9194930191614b23565b9050614c28614c2383615541565b615ca2565b90614b1e565b60068403614d2557614c41929350615876565b614c4d60c08201615885565b6fffffffffffffffffffffffffffffffff811615614cd4575b614cb7614bf960e092614c966fffffffffffffffffffffffffffffffff614c8f60a088016158a2565b92166137d1565b90614ca5610100870187610b38565b929091614cb236896158af565b615ce7565b91016fffffffffffffffffffffffffffffffff80614b4d83615885565b50614ce160a082016158a2565b15614d0b5760e0614cb7614bf9614d02614c23614cfd86613cec565b615541565b92505050614c66565b60e0614cb7614bf9614d02614c23614cfd60208701613cec565b60098403614ec857614d38929350615918565b6020810190614d478282615926565b5f939150614d5760408401615885565b614d6084613cec565b916fffffffffffffffffffffffffffffffff821615614eb4575b92919290815b614dfd57505050506060016fffffffffffffffffffffffffffffffff80614da683615885565b169216918211614db4575050565b614dce6fffffffffffffffffffffffffffffffff91615885565b7f12bacdd3000000000000000000000000000000000000000000000000000000005f521660045260245260445ffd5b829395506fffffffffffffffffffffffffffffffff614e7491614e5a614e53614e2a614e7e96978a615926565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff89019161597a565b9889615e6d565b90614e6860808b018b610b38565b94909316911590615ce7565b600f0b5f03615ca2565b937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff614eaa8692613cec565b9392019081614d80565b9050614ec2614c2383615804565b90614d7a565b60088414614eff575050505b7f5cda29d7000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b614f0a929350615876565b614f1660c08201615885565b6fffffffffffffffffffffffffffffffff811615614f9a575b614f7d614c23614f756fffffffffffffffffffffffffffffffff60e094614f5860a088016158a2565b614f66610100890189610b38565b9390921690614cb2368a6158af565b600f0b6137d1565b91016fffffffffffffffffffffffffffffffff80614da683615885565b50614fa760a082016158a2565b15614fea5760e0614f7d614c23614f756fffffffffffffffffffffffffffffffff614fdf614c23614fda60208901613cec565b615804565b945050505050614f2f565b60e0614f7d614c23614f756fffffffffffffffffffffffffffffffff614fdf614c23614fda88613cec565b92600c8103615099575061502991926157f1565b9061503381615804565b91808311615069575090613a5a917f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c9061569b565b90507f12bacdd3000000000000000000000000000000000000000000000000000000005f5260045260245260445ffd5b600f810361511c57506150ac91926157f1565b906150b681615541565b918083106150ec575090613a5a917f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c906155ad565b90507f8b063d73000000000000000000000000000000000000000000000000000000005f5260045260245260445ffd5b600b8103615175575061513390613a5a9293615529565b1561516a57615164827f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c9261565a565b9161569b565b61516482309261565a565b600e81036151a8575061519c6151926151a292613a5a9495615529565b9282949291613d00565b92615646565b916155ad565b92601084146151b957505050614ed4565b613a5a9293506151cf6151d8926151a292615529565b92919390613d00565b916151e284615541565b6149c2565b906014116103f1573560601c90565b6001600160a01b0382166001600160a01b038216105f14610ba05791565b9081602091031261032c575160ff8116810361032c5790565b60ff16604d811161039257600a0a90565b94919695968315958680156154b7575b611fa7576040517fcc56b2c50000000000000000000000000000000000000000000000000000000081526001600160a01b03938416600482015288151560248201529260209184916044918391165afa918215610fbb575f92615481575b506127106152bd6152c493836147c2565b0490613d48565b941561546a5760206001600160a01b03916004604051809481937f313ce567000000000000000000000000000000000000000000000000000000008352165afa8015610fbb576153266020916001600160a01b03935f91615453575b5061522d565b966004604051809481937f313ce567000000000000000000000000000000000000000000000000000000008352165afa8015610fbb5761536c915f91615424575061522d565b92670de0b6b3a76400008202918204670de0b6b3a76400001417156103925784615395916149b8565b90670de0b6b3a7640000810290808204670de0b6b3a7640000149015171561039257826153c1916149b8565b670de0b6b3a7640000840293808504670de0b6b3a76400001490151715610392576154198161541f93615414615403670de0b6b3a7640000996149d8996149b8565b9161540e84826159ba565b92613a16565b6159fb565b90613d48565b6147c2565b615446915060203d60201161544c575b61543e8183613792565b810190615214565b5f615320565b503d615434565b6154469150833d851161544c5761543e8183613792565b509290613d459450611f4e925061540e90826147c2565b91506020823d6020116154af575b8161549c60209383613792565b8101031261032c579051906127106152ac565b3d915061548f565b50851561524e565b916154e79183156154ea576154e0813560601c601483013560601c906151f6565b50936147d5565b91565b6154e0813560601c601583013560601c906151f6565b9160148102908082046014149015171561039257602881019182821161039257610ba093613b5c565b90606011610419578035916040602083013592013590565b61556c81307f0000000000000000000000000000000000000000000000000000000000000000615bba565b905f8212615578575090565b6001600160a01b03907f4c085bf1000000000000000000000000000000000000000000000000000000005f521660045260245ffd5b90918015615641576001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016803b1561032c575f92836064926001600160a01b03948560405198899788967f0b0d9c0900000000000000000000000000000000000000000000000000000000885216600487015216602485015260448401525af18015610fbb57613ebc5750565b505050565b908161565657613d459150615541565b5090565b907f8000000000000000000000000000000000000000000000000000000000000000820361568c57613d459150615c47565b8161565657613d459150615804565b5f918315613ad4576001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001691823b1561032c576001600160a01b03604051917fa584119400000000000000000000000000000000000000000000000000000000835216908160048201525f8160248183885af18015610fbb576157dc575b508061578f5750506020906004604051809581937f11da60b40000000000000000000000000000000000000000000000000000000083525af190811561578357506157685750565b6157809060203d602011613051576130418183613792565b50565b604051903d90823e3d90fd5b8394836020949361579f93613a3b565b6004604051809581937f11da60b40000000000000000000000000000000000000000000000000000000083525af190811561578357506157685750565b6157e99194505f90613792565b5f925f615720565b9190604011610419576020823592013590565b61582f81307f0000000000000000000000000000000000000000000000000000000000000000615bba565b905f82136158415750613d45906137d1565b6001600160a01b03907f3351b260000000000000000000000000000000000000000000000000000000005f521660045260245ffd5b90610140116104195780350190565b356fffffffffffffffffffffffffffffffff8116810361032c5790565b35801515810361032c5790565b91908260a091031261032c576040516158c781613749565b60806159138183956158d88161385b565b85526158e66020820161385b565b60208601526158f76040820161386f565b60408601526159086060820161387f565b60608601520161385b565b910152565b9060a0116104195780350190565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18136030182121561032c570180359067ffffffffffffffff821161032c57602001918160051b3603831361032c57565b9190811015610b0b5760051b810135907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff618136030182121561032c570190565b6149d8906159f5670de0b6b3a764000093846159ee816159e6816159de86896147c2565b0496806147c2565b0492806147c2565b0490613a16565b906147c2565b91925f935b60ff8510615a665760646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600260248201527f21790000000000000000000000000000000000000000000000000000000000006044820152fd5b615a7081856159ba565b83811015615b0f57615a828185613d48565b670de0b6b3a7640000810290808204670de0b6b3a7640000149015171561039257615ab28591611f4e8589615f65565b918215615ad1575b5050615ac890600192613a16565b945b0193615a00565b149050615b0857600181018082116103925783615aee82876159ba565b11615aff5750600183615ac8615aba565b93505092505090565b9350915050565b615b198482613d48565b670de0b6b3a7640000810290808204670de0b6b3a7640000149015171561039257615b498591611f4e8589615f65565b918215615b65575b5050615b5f90600192613d48565b94615aca565b1490508015615b7e575b615b0857600183615b5f615b51565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff810181811161039257615bb48491866159ba565b10615b6f565b6001600160a01b03809381602094165f52168252602460405f2060405194859384927ff135baaa0000000000000000000000000000000000000000000000000000000084526004840152165afa908115610fbb575f91615c18575090565b90506020813d602011615c3f575b81615c3360209383613792565b8101031261032c575190565b3d9150615c26565b6001600160a01b031680615c5a57504790565b6020602491604051928380927f70a082310000000000000000000000000000000000000000000000000000000082523060048301525afa908115610fbb575f91615c18575090565b906fffffffffffffffffffffffffffffffff8216809203615cbf57565b7f93dafdf1000000000000000000000000000000000000000000000000000000005f5260045ffd5b9092908315615e52576401000276a4905b60405194606086019386851067ffffffffffffffff861117613765576001600160a01b039788608095615dcd946020986040521515998a8152888101908a82528360408201931683526040519c8d998a997ff3cd914c000000000000000000000000000000000000000000000000000000008b528281511660048c0152828d8201511660248c015262ffffff60408201511660448c0152606081015160020b60648c0152015116608489015251151560a48801525160c4870152511660e48501526101206101048501526101248401916137fd565b03815f6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165af1928315610fbb575f93615e1e575b505f1303615e1857600f0b90565b60801d90565b9092506020813d602011615e4a575b81615e3a60209383613792565b8101031261032c5751915f615e0a565b3d9150615e2d565b73fffd8963efd1fc6a506488495d951d5263988d2590615cf8565b905f6080604051615e7d81613749565b8281528260208201528260408201528260608201520152615e9d82613cec565b6001600160a01b038216916001600160a01b0382168084105f14615f3457506001600160a01b03905b1680921492602081013562ffffff811680910361032c576040820135918260020b80930361032c5760600135926001600160a01b03841680940361032c576001600160a01b039060405195615f1a87613749565b865216602085015260408401526060830152608082015291565b9150506001600160a01b038291615ec6565b5f81600f0b12615cbf576fffffffffffffffffffffffffffffffff1690565b80600302600381048203610392576159ee670de0b6b3a7640000615f99819382615f9288613d45996147c2565b04906147c2565b049282615fa682806147c2565b046147c256fea264697066735822122065b1d9e315f3e430c4c81ed52de15c02c9ca2a813345b6071ec4f52d2a9fc49664736f6c634300081d0033000000000000000000000000494bbd8a3302aca833d307d11838f18dbada9c25000000000000000000000000fc0000000000000000000000000000000000000200000000000000000000000061ff070ad105d5aa6d8f9ea21212cb574eefcad500000000000000000000000061ff070ad105d5aa6d8f9ea21212cb574eefcad50000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000061ff070ad105d5aa6d8f9ea21212cb574eefcad500000000000000000000000031832f2a97fd20664d76cc421207669b55ce4bc000000000000000000000000004625b046c69577efc40e6c0bb83cdbafab5a55f558be7ee0c63546b31d0773eee1d90451bd76a0167bb89653722a2bd677c002d7b216153c50849f664871825fa6f22b3356cdce2436e4f48734ae2a926a4c7e5
Deployed Bytecode
0x6102808060405260043610156100af575b50361561001b575f80fd5b6001600160a01b037f000000000000000000000000fc00000000000000000000000000000000000002163314158061007c575b61005457005b7f38bbd576000000000000000000000000000000000000000000000000000000005f5260045ffd5b506001600160a01b037f00000000000000000000000061ff070ad105d5aa6d8f9ea21212cb574eefcad51633141561004e565b5f3560e01c9081630f54cdb614610a625750806324856bc3146109b35780633452c266146109795780633593564c14610843578063407bbdb2146108005780634aa4a4fc146107bd5780636afdd8501461077a57806379818a581461074057806391dd73461461061d57806399d8fae3146105da5780639f65a7c414610597578063aad8a4911461055d578063b7cc989c14610523578063d737d0c7146104de578063dc4c90d31461049b578063f73e5aab146104585763fa461e3314610176575f610010565b3461032c57606060031936011261032c5760243560043560443567ffffffffffffffff811161032c576101ad903690600401610a7b565b925f83139384158061044e575b610426578083019060608483031261032c5783359167ffffffffffffffff831161032c5782850181601f8201121561032c5780356101f7816137b5565b926102056040519485613792565b8184526020828401011161032c575f92816020809401848301370101526020840135926001600160a01b03841680940361032c57604085013592831515840361032c5763ffffffff1685019463ffffffff86351692602087019160208886010191011061041957601791602b84106103f15781359861029a8662ffffff60378d60601c9b013560601c9c60481c168c8b613926565b6001600160a01b03339116036103c957156103bf57508786105b156102cb57505050506102c993503391613a3b565b005b9193945091949582602b0180602b11610392578410610330575082821161032c5781019103907f800000000000000000000000000000000000000000000000000000000000000085101561032c576102c99461032733916137d1565b613b74565b5f80fd5b939594505050507faf28d9864a81dfdf71cab65f4e5d79a0cf9b083905fb8971425e6cb581b3f6925c821161036a576102c9923391613a3b565b7f739dbe52000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b96508588106102b4565b7f32b13d91000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f3b99b53d000000000000000000000000000000000000000000000000000000005f5260045ffd5b633b99b53d5f526004601cfd5b7f316cf0eb000000000000000000000000000000000000000000000000000000005f5260045ffd5b505f8213156101ba565b3461032c575f60031936011261032c5760206040516001600160a01b037f00000000000000000000000061ff070ad105d5aa6d8f9ea21212cb574eefcad5168152f35b3461032c575f60031936011261032c5760206040516001600160a01b037f00000000000000000000000061ff070ad105d5aa6d8f9ea21212cb574eefcad5168152f35b3461032c575f60031936011261032c5760207f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c6001600160a01b0360405191168152f35b3461032c575f60031936011261032c5760206040517f558be7ee0c63546b31d0773eee1d90451bd76a0167bb89653722a2bd677c002d8152f35b3461032c575f60031936011261032c5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b3461032c575f60031936011261032c5760206040516001600160a01b037f00000000000000000000000004625b046c69577efc40e6c0bb83cdbafab5a55f168152f35b3461032c575f60031936011261032c5760206040516001600160a01b037f00000000000000000000000061ff070ad105d5aa6d8f9ea21212cb574eefcad5168152f35b3461032c57602060031936011261032c5760043567ffffffffffffffff811161032c5761064e903690600401610a7b565b6001600160a01b037f00000000000000000000000061ff070ad105d5aa6d8f9ea21212cb574eefcad51633036107185761068791613ec6565b908183036106f0575f5b8381106106c4576106c06040516106a9602082613792565b5f8152604051918291602083526020830190610ada565b0390f35b806106ea6106d56001938789610aff565b3560f81c6106e4838787610b89565b91614abc565b01610691565b7faaad13f7000000000000000000000000000000000000000000000000000000005f5260045ffd5b7fae18210a000000000000000000000000000000000000000000000000000000005f5260045ffd5b3461032c575f60031936011261032c5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b3461032c575f60031936011261032c5760206040516001600160a01b037f000000000000000000000000494bbd8a3302aca833d307d11838f18dbada9c25168152f35b3461032c575f60031936011261032c5760206040516001600160a01b037f000000000000000000000000fc00000000000000000000000000000000000002168152f35b3461032c575f60031936011261032c5760206040516001600160a01b037f00000000000000000000000031832f2a97fd20664d76cc421207669b55ce4bc0168152f35b606060031936011261032c5760043567ffffffffffffffff811161032c5761086f903690600401610a7b565b60243567ffffffffffffffff811161032c5761088f903690600401610aa9565b91604435421161095157333014610948576001600160a01b037f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c16610920576108fb93337f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085d610ba4565b5f7f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085d005b7f6f5ffb7e000000000000000000000000000000000000000000000000000000005f5260045ffd5b6102c993610ba4565b7f5bf6f916000000000000000000000000000000000000000000000000000000005f5260045ffd5b3461032c575f60031936011261032c5760206040517f7b216153c50849f664871825fa6f22b3356cdce2436e4f48734ae2a926a4c7e58152f35b604060031936011261032c5760043567ffffffffffffffff811161032c576109df903690600401610a7b565b60243567ffffffffffffffff811161032c576109ff903690600401610aa9565b91333014610948576001600160a01b037f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c16610920576108fb93337f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085d610ba4565b3461032c575f60031936011261032c5780600a60209252f35b9181601f8401121561032c5782359167ffffffffffffffff831161032c576020838186019501011161032c57565b9181601f8401121561032c5782359167ffffffffffffffff831161032c576020808501948460051b01011161032c57565b90601f19601f602080948051918291828752018686015e5f8582860101520116010190565b90821015610b0b570190565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18136030182121561032c570180359067ffffffffffffffff821161032c5760200191813603831361032c57565b90821015610b0b57610ba09160051b810190610b38565b9091565b90808403613721579392915f610160525b846101605110610bc6575050509050565b610bd4610160518684610aff565b3593610be4610160518584610b89565b6101c0919091526060610180526001949060f887901c603f1660218110156135c8576010811015612af25760088110156117c957806110815750610c2b906101c0516138fb565b93909460806101c05101355f1461107a577f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c955b610c6d6101c05135613d00565b969086816102605260206101c0510135917f800000000000000000000000000000000000000000000000000000000000000060206101c051013514610fe8575b50505b602b6042881015610240527f800000000000000000000000000000000000000000000000000000000000000082101561032c576102405115610fe15730915b88821161032c576040916001600160a01b035f610260513595610de5610d6d610d9085610d418b60601c9b601761026051013560601c9c8d81109d62ffffff60a06101c05101359360481c1691613926565b16968a8614610fc6576401000276a49b5b878b51948593606060208601526080850190610260516137fd565b91168b83015260a06101c05101351515606083015203601f198101835282613792565b8488519a8b98899788967f128acb080000000000000000000000000000000000000000000000000000000088521660048701528b6024870152604486015216606484015260a0608484015260a4830190610ada565b03925af1908115610fbb575f905f92610f7f575b610e09935015610f7857506137d1565b94610240515f14610e505730908060171161032c57610260805160170190527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe90195610cb0565b50949095919692979360406101c051013511610f50576001600160a01b03807f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c169116907faa2dd386ed08047486db39ea1a9e5461b6a95c8bb3ca845cce27a537799a46725f80a35b159081610f25575b50610eda576001610160510161016052939291610bb5565b6040517f2c4029e90000000000000000000000000000000000000000000000000000000081526101605160048201526040602482015280610f216044820161018051610ada565b0390fd5b7f8000000000000000000000000000000000000000000000000000000000000000915016155f610ec2565b7f39d35496000000000000000000000000000000000000000000000000000000005f5260045ffd5b90506137d1565b9150506040823d8211610fb3575b81610f9a60409383613792565b8101031261032c57816020610e09935191015191610df9565b3d9150610f8d565b6040513d5f823e3d90fd5b73fffd8963efd1fc6a506488495d951d5263988d259b610d52565b8891610cef565b602492506110006020926001600160a01b03926151e7565b16604051928380927f70a082310000000000000000000000000000000000000000000000000000000082523060048301525afa908115610fbb575f91611049575b505f80610cad565b90506020813d8211611072575b8161106360209383613792565b8101031261032c57515f611041565b3d9150611056565b3095610c60565b6001819995979293989499145f146112265750906110aa60206101c0510135916101c0516138fb565b6101c051919291608001351561121f577f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c925b6110eb6101c05135613d00565b9360406101c05101357faf28d9864a81dfdf71cab65f4e5d79a0cf9b083905fb8971425e6cb581b3f6925d7f800000000000000000000000000000000000000000000000000000000000000084101561032c576111559260a06101c05101359286610327876137d1565b909190156112105750611167906137d1565b036111e8575f7faf28d9864a81dfdf71cab65f4e5d79a0cf9b083905fb8971425e6cb581b3f6925d6001600160a01b03807f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c169116907faa2dd386ed08047486db39ea1a9e5461b6a95c8bb3ca845cce27a537799a46725f80a35b610eba565b7fd4e0248e000000000000000000000000000000000000000000000000000000005f5260045ffd5b61121a91506137d1565b611167565b30926110de565b6002810361127a575090506111e37f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c60406101c05101359061126f60206101c0510135613d00565b906101c05135613e24565b600381036114655750506101c051356101c0510163ffffffff60206101c0510135166101c051019063ffffffff82351692602080840193850101906101c0510110610419577f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c92604051926001600160a01b0360208501957f2a2d80d10000000000000000000000000000000000000000000000000000000087521660248501526060604485015260e484019280357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18236030181121561032c5781016020813591019467ffffffffffffffff821161032c578160071b3603861361032c5781906060608489015252610104860194905f905b80821061144757505050936113f95f96948294611407946040896001600160a01b036113bd60208e9d0161385b565b1660a4880152013560c48601527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc8584030160648601526137fd565b03601f198101835282613792565b5190826001600160a01b037f000000000000000000000000494bbd8a3302aca833d307d11838f18dbada9c25165af161143e61381d565b61018052610eba565b9091956080808261145a6001948b6138a0565b01970192019061138e565b909150600481036115bc575061148160206101c0510135613d00565b6001600160a01b0360406101c0510135166001600160a01b036101c051351680155f146114f65750479081106114ce57806114be575b5050610eba565b6114c791614093565b5f806114b7565b7f6a12f104000000000000000000000000000000000000000000000000000000005f5260045ffd5b91604051917f70a08231000000000000000000000000000000000000000000000000000000008352306004840152602083602481875afa928315610fbb575f93611589575b5082106115615781611550575b505050610eba565b61155992614a04565b5f8080611548565b7f675cae38000000000000000000000000000000000000000000000000000000005f5260045ffd5b9092506020813d82116115b4575b816115a460209383613792565b8101031261032c5751915f61153b565b3d9150611597565b600581036115ea57506111e360406101c05101356115e060206101c0510135613d00565b6101c05135613d55565b600681036116bd575060406101c051013561160b60206101c0510135613d00565b6101c051356001600160a01b031680611632575061162c6111e392476149c2565b90614093565b906040517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152602081602481865afa908115610fbb575f9161168a575b506111e393611684916149c2565b91614a04565b90506020813d82116116b5575b816116a460209383613792565b8101031261032c57516111e3611676565b3d9150611697565b6007036111e3576101c0513560406101c05101357f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c7f80000000000000000000000000000000000000000000000000000000000000008214611736575b916111e39261173060206101c0510135613d00565b91613a3b565b9050604051917f70a082310000000000000000000000000000000000000000000000000000000083526001600160a01b03821660048401526020836024816001600160a01b0385165afa8015610fbb575f90611796575b9092509061171b565b506020833d82116117c1575b816117af60209383613792565b8101031261032c576111e3925161178d565b3d91506117a2565b60088199959793989499145f14611c56575060206101c05101356117f860a06101c0510135926101c0516138fb565b919060806101c05101355f14611c4f577f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c905b6118396101c05135613d00565b928185017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec013560601c928615611b7d57602886811161032c576001600160a01b03907f00000000000000000000000061ff070ad105d5aa6d8f9ea21212cb574eefcad5906119937f0000000000000000000000000000000000000000000000000000000000000000916118d8601489013560601c893560601c6151f6565b90917fffffffffffffffffffffffffffffffffffffffff0000000000000000000000006040519281602085019560601b16855260601b1660348301528152611921604882613792565b5190206113f9604051938492602084019687917fffffffffffffffffffffffffffffffffffffffff000000000000000000000000605594927fff00000000000000000000000000000000000000000000000000000000000000855260601b166001840152601583015260358201520190565b51902016915b8281611b62575b505050604051937f70a082310000000000000000000000000000000000000000000000000000000085526001600160a01b03811696876004870152602086602481885afa958615610fbb575f96611b22575b506024966020959493929115611b1457611a0b936144dc565b604051938480927f70a082310000000000000000000000000000000000000000000000000000000082528760048301525afa918215610fbb575f92611adf575b50611a5e9060406101c051013592613d48565b10611ab7576001600160a01b037f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c167faa2dd386ed08047486db39ea1a9e5461b6a95c8bb3ca845cce27a537799a46725f80a3610eba565b7f849eaf98000000000000000000000000000000000000000000000000000000005f5260045ffd5b9091506020813d8211611b0c575b81611afa60209383613792565b8101031261032c575190611a5e611a4b565b3d9150611aed565b611b1d93614147565b611a0b565b949392919095506020853d8211611b5a575b81611b4160209383613792565b8101031261032c579351949293919290919060246119f2565b3d9150611b34565b611b7592611b7089876151e7565b613a3b565b5f80826119a0565b602986811161032c576001600160a01b03907f00000000000000000000000031832f2a97fd20664d76cc421207669b55ce4bc090611c457f558be7ee0c63546b31d0773eee1d90451bd76a0167bb89653722a2bd677c002d91611beb601589013560601c893560601c6151f6565b90917fffffffffffffffffffffffffffffffffffffffff0000000000000000000000006040519281602085019560601b16855260601b16603483015260148a01355f1a151560f81b60488301528152611921604982613792565b5190201691611999565b309061182c565b92979196939594939192600981036124035750611c76906101c0516138fb565b610200529360806101c05101355f146123fc577f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c935b611cba6101c05135613d00565b966102005161022052866080525f6101e0525f60c0525f60c05260a06101c05101355f146123f057602861020051105b6123c8576101c05160208101356101e05260a0013515612116575b60286102205110612025577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd8610220510161022051811161039257611d5590610220519061022051608051613b5c565b6101a052611d686101a0518260016154bf565b90610120526101205150604051907f0902f1ac0000000000000000000000000000000000000000000000000000000082526060826004816001600160a01b0361012051165afa928315610fbb575f925f94611fe1575b506001600160a01b03611dd681926101a051906151e7565b925f61010052169116145f14611fd757610100525b610100516101205160c0525f9190158015611fcf575b611fa7576101c05160a0013515611f2b57611e226101e051610100516147c2565b906103e88202918083046103e81490151715611efe576101e051611e4591613d48565b906103e58202918083046103e51490151715611efe5790611e65916149b8565b9060018201809211611ed157505b6101e052610220517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec8101116103925761022080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec019052611d05565b807f4e487b7100000000000000000000000000000000000000000000000000000000602492526011600452fd5b6024837f4e487b710000000000000000000000000000000000000000000000000000000081526011600452fd5b611f549150611f4e611f43610100516101e0516147c2565b916101e05190613d48565b906149b8565b6127108102908082046127101490151715610392576126f290046001810180911115611e73577f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b7f7b9c8916000000000000000000000000000000000000000000000000000000005f5260045ffd5b508015611e01565b9061010052611deb565b6001600160a01b03919450611dd69350612012829160603d811161201e575b61200a8183613792565b810190614100565b50959094509150611dbe565b503d612000565b949893989792979691969590955b60406101c05101356101e051116120ee5761205e906101e0519060c05190611b7061020051866151e7565b6101c05160a00135156120d65761207e908260c0519161020051906144dc565b6001600160a01b03807f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c169116907faa2dd386ed08047486db39ea1a9e5461b6a95c8bb3ca845cce27a537799a46725f80a3610eba565b6120e9908260c051916102005190614147565b61207e565b7f8ab0bc16000000000000000000000000000000000000000000000000000000005f5260045ffd5b602961022051106123b657602961022051036102205181116103925761214790610220519061022051608051613b5c565b906101405260146101405101355f1a60e05261216781610140515f6154bf565b906040517f0902f1ac0000000000000000000000000000000000000000000000000000000081526060816004816001600160a01b0386165afa928315610fbb5761222e945f925f95612382575b506001600160a01b036121cb8192610140516151e7565b92169116145f146123795760209092915b60c08190526040517fcc56b2c500000000000000000000000000000000000000000000000000000000815260e0516001600160a01b039092166004820152901515602482015293849081906044820190565b03816001600160a01b037f00000000000000000000000031832f2a97fd20664d76cc421207669b55ce4bc0165afa928315610fbb575f93612346575b508115801561233e575b611fa75760e05161231657611f4e611f43612292936101e0516147c2565b90612710820291808304612710149015171561039257612710036127108111610392576122be916149b8565b60018101809111610392576101e052610220517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeb810160a081905211610392576102205160a0511161032c5760a05161022052612116565b7fbd534ea5000000000000000000000000000000000000000000000000000000005f5260045ffd5b508015612274565b9092506020813d8211612371575b8161236160209383613792565b8101031261032c5751915f61226a565b3d9150612354565b906020906121dc565b6001600160a01b039195506121cb93506123aa829160603d811161201e5761200a8183613792565b509690945091506121b4565b94989398979297969196959095612033565b7f20db8267000000000000000000000000000000000000000000000000000000005f5260045ffd5b60296102005110611cea565b3093611cad565b92979196949593949192600a81036124ff5750905063ffffffff60c06101c0510135166101c0510163ffffffff81351691602080830192840101906101c0510110610419575f9182916114077f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c926113f96040519384926001600160a01b0360208501977f2b67b5700000000000000000000000000000000000000000000000000000000089521660248501526124c1604485016101c0516138a0565b6001600160a01b036124d860806101c0510161385b565b1660c485015260a06101c051013560e48501526101006101048501526101248401916137fd565b600b810361269b57505060206101c051013561251e6101c05135613d00565b90807f8000000000000000000000000000000000000000000000000000000000000000810361266c575050475b80612557575050610eba565b6001600160a01b037f000000000000000000000000fc000000000000000000000000000000000000021691823b1561032c57604051907fd0e30db00000000000000000000000000000000000000000000000000000000082525f8260048186885af1918215610fbb576001600160a01b039261265c575b5016903082036125df575b506114b7565b60446020925f60405195869485937fa9059cbb000000000000000000000000000000000000000000000000000000008552600485015260248401525af18015610fbb5761262e575b80806125d9565b61264e9060203d8111612655575b6126468183613792565b810190613a23565b505f612627565b503d61263c565b5f61266691613792565b5f6125ce565b47101561254b577f6a12f104000000000000000000000000000000000000000000000000000000005f5260045ffd5b600c81036127ef5750506126b26101c05135613d00565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201527f000000000000000000000000fc000000000000000000000000000000000000026001600160a01b0316602082602481845afa918215610fbb575f926127bc575b5060206101c051013582106114ce578161273b57505050610eba565b803b1561032c575f80916024604051809481937f2e1a7d4d0000000000000000000000000000000000000000000000000000000083528760048401525af18015610fbb576127ac575b50306001600160a01b0383160361279c575b80611548565b6127a591614093565b5f80612796565b5f6127b691613792565b5f612784565b9091506020813d82116127e7575b816127d760209383613792565b8101031261032c5751905f61271f565b3d91506127ca565b600d81999395989299969496145f146129dd57506101c051356101c051019586359861282360206101c0518a03018b613a16565b116103f1576001600160a01b037f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c1697935f5b8a8110156128af57896001600160a01b0361287960208460071b8d0101613cec565b160361288757600101612857565b7fe7002877000000000000000000000000000000000000000000000000000000005f5260045ffd5b509593949298919750956001600160a01b037f000000000000000000000000494bbd8a3302aca833d307d11838f18dbada9c251690813b1561032c57916040519283917f0d58b1db000000000000000000000000000000000000000000000000000000008352806024840160206004860152526020604484019201905f905b8082106129635750505091815f81819503925af18015610fbb57612953575b50610eba565b5f61295d91613792565b5f61294d565b919350916080806001926001600160a01b0361297e8861385b565b1681526001600160a01b036129956020890161385b565b1660208201526001600160a01b036129af6040890161385b565b1660408201526001600160a01b036129c96060890161385b565b16606082015201940192018593929161292e565b919750959193919250600e8103612ac757506040517f70a082310000000000000000000000000000000000000000000000000000000081526001600160a01b036101c051351660048201526020816024816001600160a01b03846101c0510135165afa908115610fbb575f91612a96575b506101c051604001351115806111e3576040517fa32816720000000000000000000000000000000000000000000000000000000060208201526004815261143e602482613792565b90506020813d8211612abf575b81612ab060209383613792565b8101031261032c57515f612a4e565b3d9150612aa3565b7fd76a1e9e000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b60108199959793989499145f14612bfd57505f612b4891604051809381927f48c894910000000000000000000000000000000000000000000000000000000083526020600484015260248301906101c0516137fd565b0381836001600160a01b037f00000000000000000000000061ff070ad105d5aa6d8f9ea21212cb574eefcad5165af18015610fbb57612b875750610eba565b3d805f833e612b968183613792565b81019060208183031261032c5780519067ffffffffffffffff821161032c570181601f8201121561032c578051612bcc816137b5565b92612bda6040519485613792565b8184526020828401011161032c575f928160208094018483015e0101525f61294d565b60118103612d15575050505f8060405160208101907f6276cbbe0000000000000000000000000000000000000000000000000000000082526001600160a01b03612c496101c05161385b565b1660248201526001600160a01b03612c6660206101c0510161385b565b16604482015262ffffff612c7f60406101c0510161386f565b166064820152612c9460606101c0510161387f565b60020b60848201526001600160a01b03612cb360806101c0510161385b565b1660a48201526001600160a01b0360a06101c05101351660c482015260c48152612cde60e482613792565b5190826001600160a01b037f00000000000000000000000061ff070ad105d5aa6d8f9ea21212cb574eefcad5165af161143e61381d565b6012810361341c5750506101c0517f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c9060408101359060808101359060c08101359060608101359060208101359060a08101359060e00135156134155786915b6001600160a01b0381166001036134105750865b7f80000000000000000000000000000000000000000000000000000000000000008614613383575b6001600160a01b031695861561335b576101c0513560ff166001810361314657506001600160a01b03841691604051937f996c6cc3000000000000000000000000000000000000000000000000000000008552602085600481875afa8015610fbb575f906130fc575b6001600160a01b038481169650168590036130d45787612e3c918785613f5e565b604051907f775313a100000000000000000000000000000000000000000000000000000000825263ffffffff871690816004840152602083602481885afa948515610fbb578b935f9661309e575b50612f23607660209788604051612ea18282613792565b5f81527fffffffffffffffffffffffffffffffffffffffff000000000000000000000000604051998a947e01000000000000000000000000000000000000000000000000000000000000858701525f6022870152604286015260601b1660628401528051918291018484015e81015f838201520301601f198101865285613792565b6040517f7f5a7c7b0000000000000000000000000000000000000000000000000000000081528681600481855afa928315610fbb5787948c948e935f91613058575b506001600160a01b03612fbe604051998a98899788967f51debffc00000000000000000000000000000000000000000000000000000000885260048801526024870152604486015260a0606486015260a4850190610ada565b9116608483015203925af1958615610fbb576130156040966001600160a01b0396879663ffffffff957f7bdf8294c8b336e56cb4381cf529bf217d329da7a3b80e3705bf4da8ba7aa8569b61302a575b5050614039565b855197885216602087015216951692a4610eba565b8161304992903d10613051575b6130418183613792565b81019061384c565b505f8061300e565b503d613037565b93505093509381813d8311613097575b6130728183613792565b8101031261032c57516001600160a01b038116810361032c5786938b938d925f612f65565b503d613068565b935094506020833d82116130cc575b816130ba60209383613792565b8101031261032c578a9251945f612e8a565b3d91506130ad565b7f1eb00b06000000000000000000000000000000000000000000000000000000005f5260045ffd5b50936020813d821161313e575b8161311660209383613792565b8101031261032c5751936001600160a01b038516850361032c576001600160a01b0394612e1b565b3d9150613109565b9193929160028103613330575046600a036132bd57604051917f785e9e860000000000000000000000000000000000000000000000000000000083526020836004816001600160a01b0388165afa8015610fbb576001600160a01b03935f9161328f575b505b83831693849116036130d457866131c4918584613f5e565b6001600160a01b03831693843b1561032c57886084895f936001600160a01b0398604051998a9586947fdbf2e4b700000000000000000000000000000000000000000000000000000000865260048601528d602486015263ffffffff8d1660448601521660648401525af1938415610fbb577f7bdf8294c8b336e56cb4381cf529bf217d329da7a3b80e3705bf4da8ba7aa8569561327a6001600160a01b0395869563ffffffff9460409961327f575b50614039565b613015565b5f61328991613792565b5f613274565b6132b0915060203d81116132b6575b6132a88183613792565b810190613ccd565b5f6131aa565b503d61329e565b604051917fffcbd8d00000000000000000000000000000000000000000000000000000000083526020836004816001600160a01b0388165afa8015610fbb576001600160a01b03935f91613312575b506131ac565b61332a915060203d81116132b6576132a88183613792565b5f61330c565b7f6a85a5d8000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b7f9c8d2cd2000000000000000000000000000000000000000000000000000000005f5260045ffd5b94506040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526020816024816001600160a01b038b165afa8015610fbb575f906133d8575b959050612db2565b506020813d8211613408575b816133f160209383613792565b8101031261032c576001600160a01b0390516133d0565b3d91506133e4565b612d8a565b3091612d76565b60138103612ac7575060806101c051013563ffffffff60e06101c0510135166101c051019063ffffffff82351692602080840193850101906101c0510110610419576001600160a01b0360206101c0510135169160406135076001600160a01b037f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c1692825193849283927fa53e921000000000000000000000000000000000000000000000000000000000845263ffffffff6101c0513516998a6004860152866101c0510135602486015260606101c0510135604486015260e0606486015260e48501916137fd565b906001600160a01b0360c06101c051013516608484015260a48301528560c4830152038160a06101c0510135875af18015610fbb5761359d575b507f8907eccad16a44af80785607b6783b3878f6973673e4d0e86bc915d0bf9bd9c860206001600160a01b037f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c1692604051908152a4610eba565b604090813d81116135c1575b6135b38183613792565b8101031261032c575f613541565b503d6135a9565b60218199959793989499145f14612ac757506135e7906101c051613ec6565b61362a6040959395519460208601967f24856bc30000000000000000000000000000000000000000000000000000000088526040602488015260648701916137fd565b927fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc858503016044860152818452602084019160208160051b86010194845f907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1813603015b8483106136c557505050505050505091816136b75f9493859403601f198101835282613792565b519082305af161143e61381d565b9091929394959697601f1985820301885288358281121561032c578301906020823592019167ffffffffffffffff811161032c57803603831361032c5761371160209283928b956137fd565b9a01980196959493019190613690565b7fff633a38000000000000000000000000000000000000000000000000000000005f5260045ffd5b60a0810190811067ffffffffffffffff82111761376557604052565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b90601f601f19910116810190811067ffffffffffffffff82111761376557604052565b67ffffffffffffffff811161376557601f01601f191660200190565b7f80000000000000000000000000000000000000000000000000000000000000008114610392575f0390565b601f8260209493601f1993818652868601375f8582860101520116010190565b3d15613847573d9061382e826137b5565b9161383c6040519384613792565b82523d5f602084013e565b606090565b9081602091031261032c575190565b35906001600160a01b038216820361032c57565b359062ffffff8216820361032c57565b35908160020b820361032c57565b359065ffffffffffff8216820361032c57565b65ffffffffffff6138f5606080936001600160a01b036138bf8261385b565b1686526001600160a01b036138d66020830161385b565b166020870152836138e96040830161388d565b1660408701520161388d565b16910152565b909163ffffffff60608301351682019263ffffffff8435169260208086019585010191011061041957565b6001600160a01b039392919284841685831611613a0e575b6139ba9190156139c1577f00000000000000000000000061ff070ad105d5aa6d8f9ea21212cb574eefcad59362ffffff7f0000000000000000000000000000000000000000000000000000000000000000945b876040519381602086019616865216604084015216606082015260608152611921608082613792565b5190201690565b7f00000000000000000000000004625b046c69577efc40e6c0bb83cdbafab5a55f9362ffffff7f7b216153c50849f664871825fa6f22b3356cdce2436e4f48734ae2a926a4c7e594613991565b90929061393e565b9190820180921161039257565b9081602091031261032c5751801515810361032c5790565b9291906001600160a01b038116308103613a5c575050613a5a92613d55565b565b5f809160409695965160208101917f23b872dd00000000000000000000000000000000000000000000000000000000835260248201526001600160a01b038616604482015287606482015260648152613ab6608482613792565b519082875af1613ac461381d565b9015908115613b2c575b50613ada575b50505050565b6001600160a01b038411613b04576001600160a01b03613afb941692613e24565b5f808080613ad4565b7fc4bd89a9000000000000000000000000000000000000000000000000000000005f5260045ffd5b8051801515925082613b41575b50505f613ace565b613b549250602080918301019101613a23565b155f80613b39565b9093929384831161032c57841161032c578101920390565b949390919293602b85106103f1578335948560601c93601786013560601c948381871096879960481c62ffffff1690613bac93613926565b6001600160a01b031693855f14966001600160a01b038095613c115f96613c649560409c613cb2576401000276a4935b85613bf68f519889956060602088015260808701916137fd565b92168e8401521515606083015203601f198101855284613792565b89519b8c998a9889977f128acb080000000000000000000000000000000000000000000000000000000089521660048801526024870152604486015216606484015260a0608484015260a4830190610ada565b03925af18015610fbb575f925f91613c7b57509192565b9250506040823d604011613caa575b81613c9760409383613792565b8101031261032c57602082519201519192565b3d9150613c8a565b73fffd8963efd1fc6a506488495d951d5263988d2593613bdc565b9081602091031261032c57516001600160a01b038116810361032c5790565b356001600160a01b038116810361032c5790565b6001600160a01b03811660018103613d395750507f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c90565b600203613d4557503090565b90565b9190820391821161039257565b9091906001600160a01b031680613d705750613a5a91614093565b7f80000000000000000000000000000000000000000000000000000000000000008214613da2575b91613a5a92614a04565b9050604051917f70a08231000000000000000000000000000000000000000000000000000000008352306004840152602083602481855afa8015610fbb575f90613df0575b90925090613d98565b506020833d602011613e1c575b81613e0a60209383613792565b8101031261032c57613a5a9251613de7565b3d9150613dfd565b91926001600160a01b037f000000000000000000000000494bbd8a3302aca833d307d11838f18dbada9c251691823b1561032c575f6001600160a01b039384829681608496816040519b8c9a8b997f36c78516000000000000000000000000000000000000000000000000000000008b521660048a01521660248801521660448601521660648401525af18015610fbb57613ebc5750565b5f613a5a91613792565b604081351891606082019363ffffffff6040840135169363ffffffe0601f8601169060608201602086013518179084019260608401359463ffffffff861694641fffffffe0608082019760051b1680915f925b808410613f3157506080925001019101101761041957565b90916020809163ffffffe0601f60808089890101359b848d18179b88010135011601019301929190613f19565b905f6001600160a01b036044936020968396813085831603614026575b505082604051957f095ea7b30000000000000000000000000000000000000000000000000000000087521660048601526024850152165af13d15601f3d1160015f511416171615613fc857565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f415050524f56455f4641494c45440000000000000000000000000000000000006044820152fd5b61403291309085613a3b565b5f81613f7b565b60446020925f80936001600160a01b03604051937f095ea7b30000000000000000000000000000000000000000000000000000000085521660048401528160248401525af13d15601f3d1160015f511416171615613fc857565b5f80809381935af1156140a257565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f4554485f5452414e534645525f4641494c4544000000000000000000000000006044820152fd5b9081606091031261032c578051916040602083015192015190565b90613d4594936080936001600160a01b0392845260208401521660408201528160608201520190610ada565b91927fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec820191808311610392576015928390049182156144875760298083811161032c575091959490916141a785830135606090811c908435901c6151f6565b50965f926020985b8685106141c25750505050505050505050565b87850285810489148615171561039257848101808211610392576141e7918585613b5c565b506040517f0902f1ac0000000000000000000000000000000000000000000000000000000081526060816004816001600160a01b038c165afa908115610fbb575f905f92614465575b50823560601c6001600160a01b0385160361446057905b604051907f70a082310000000000000000000000000000000000000000000000000000000082526001600160a01b038a1660048301528d82602481873560601c5afa908115610fbb578c925f92614427575b50926142e892916001600160a01b039460148701355f1a9387013560601c928d81893560601c9403907f00000000000000000000000031832f2a97fd20664d76cc421207669b55ce4bc061523e565b9216903560601c145f14614420575f5b8a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff890187101561441757506001860191898302928084048b1490151715610392578583019182841161039257614355614376938e958888613b5c565b90939061436e8d860135606090811c908735901c6151f6565b50945f6147d5565b925b986143866040519586613792565b5f8552601f198e01368f8701376001600160a01b0381163b1561032c575f92836001600160a01b0386946143e9604051998a97889687947f022c0d9f0000000000000000000000000000000000000000000000000000000086526004860161411b565b0393165af1918215610fbb57600192614407575b50940193946141af565b5f61441191613792565b5f6143fd565b918a915f614378565b5f906142f8565b8f809294508193503d8311614459575b6144418183613792565b8101031261032c57518b916001600160a01b03614299565b503d614437565b614247565b905061447f915060603d811161201e5761200a8183613792565b50905f614230565b7fae52ad0c000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b9290919260288310614487576144fe813560601c601483013560601c906151f6565b509160148404937ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff86019501925f91602097601f198901965b88851061456b5750505050505050505050565b6001600160a01b0361457e868587615500565b503560601c961695604051907f0902f1ac0000000000000000000000000000000000000000000000000000000082526060826004818b5afa928315610fbb578c905f935f95614796575b506001600160a01b0316821492831561478c579060249194925b604051928380927f70a082310000000000000000000000000000000000000000000000000000000082528d60048301525afa908115610fbb575f9161475f575b5083810391841591828015614757575b611fa757856103e585029485046103e514911417156103925761465590836147c2565b906103e885029485046103e814171561039257611f4e614676928e95613a16565b9015614751575f905b88871015614749576146bd91614699600189018789615500565b5f949194506146b4853560601c601487013560601c906151f6565b509460016147d5565b925b988b604051956146cf8188613792565b5f87523690870137803b1561032c575f9283859361471c604051988996879586947f022c0d9f0000000000000000000000000000000000000000000000000000000086526004860161411b565b03925af1918215610fbb57600192614739575b5094019394614558565b5f61474391613792565b5f61472f565b83915f6146bf565b5f61467f565b508115614632565b90508c81813d8311614785575b6147768183613792565b8101031261032c57515f614622565b503d61476c565b91602491906145e2565b6147b99195506001600160a01b03945060603d811161201e5761200a8183613792565b509490936145c8565b8181029291811591840414171561039257565b6001600160a01b039250156148f8577f00000000000000000000000061ff070ad105d5aa6d8f9ea21212cb574eefcad5906139ba6148407f000000000000000000000000000000000000000000000000000000000000000092601481013560601c903560601c6151f6565b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000006040519181602084019460601b16845260601b16603482015260288152611921604882613792565b6113f9604051938492602084019687917fffffffffffffffffffffffffffffffffffffffff000000000000000000000000605594927fff00000000000000000000000000000000000000000000000000000000000000855260601b166001840152601583015260358201520190565b7f00000000000000000000000031832f2a97fd20664d76cc421207669b55ce4bc0906139ba7f558be7ee0c63546b31d0773eee1d90451bd76a0167bb89653722a2bd677c002d916014614956601583013560601c833560601c6151f6565b9190927fffffffffffffffffffffffffffffffffffffffff0000000000000000000000006040519381602086019660601b16865260601b16603484015201355f1a151560f81b6048820152602981526149b0604982613792565b519020614889565b81156144af570490565b61271082116149dc57612710916149d8916147c2565b0490565b7fdeaa01e6000000000000000000000000000000000000000000000000000000005f5260045ffd5b5f91826044926020956001600160a01b03604051947fa9059cbb00000000000000000000000000000000000000000000000000000000865216600485015260248401525af13d15601f3d1160015f511416171615614a5e57565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f5452414e534645525f4641494c454400000000000000000000000000000000006044820152fd5b929190600b8410156150155760078403614c2e57614adb929350615918565b6020810190614aea8282615926565b90505f92614af783613cec565b90614b0460408501615885565b906fffffffffffffffffffffffffffffffff821615614c15575b92915f915b838310614ba45750505050506060016fffffffffffffffffffffffffffffffff80614b4d83615885565b169216918210614b5b575050565b614b756fffffffffffffffffffffffffffffffff91615885565b7f8b063d73000000000000000000000000000000000000000000000000000000005f521660045260245260445ffd5b8496506fffffffffffffffffffffffffffffffff614bf991614be0614bd986614bd3614bfe9798999a8c615926565b9061597a565b9586615e6d565b614bed6080880188610b38565b949093165f0391615ce7565b615f46565b946001614c0b8793613cec565b9194930191614b23565b9050614c28614c2383615541565b615ca2565b90614b1e565b60068403614d2557614c41929350615876565b614c4d60c08201615885565b6fffffffffffffffffffffffffffffffff811615614cd4575b614cb7614bf960e092614c966fffffffffffffffffffffffffffffffff614c8f60a088016158a2565b92166137d1565b90614ca5610100870187610b38565b929091614cb236896158af565b615ce7565b91016fffffffffffffffffffffffffffffffff80614b4d83615885565b50614ce160a082016158a2565b15614d0b5760e0614cb7614bf9614d02614c23614cfd86613cec565b615541565b92505050614c66565b60e0614cb7614bf9614d02614c23614cfd60208701613cec565b60098403614ec857614d38929350615918565b6020810190614d478282615926565b5f939150614d5760408401615885565b614d6084613cec565b916fffffffffffffffffffffffffffffffff821615614eb4575b92919290815b614dfd57505050506060016fffffffffffffffffffffffffffffffff80614da683615885565b169216918211614db4575050565b614dce6fffffffffffffffffffffffffffffffff91615885565b7f12bacdd3000000000000000000000000000000000000000000000000000000005f521660045260245260445ffd5b829395506fffffffffffffffffffffffffffffffff614e7491614e5a614e53614e2a614e7e96978a615926565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff89019161597a565b9889615e6d565b90614e6860808b018b610b38565b94909316911590615ce7565b600f0b5f03615ca2565b937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff614eaa8692613cec565b9392019081614d80565b9050614ec2614c2383615804565b90614d7a565b60088414614eff575050505b7f5cda29d7000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b614f0a929350615876565b614f1660c08201615885565b6fffffffffffffffffffffffffffffffff811615614f9a575b614f7d614c23614f756fffffffffffffffffffffffffffffffff60e094614f5860a088016158a2565b614f66610100890189610b38565b9390921690614cb2368a6158af565b600f0b6137d1565b91016fffffffffffffffffffffffffffffffff80614da683615885565b50614fa760a082016158a2565b15614fea5760e0614f7d614c23614f756fffffffffffffffffffffffffffffffff614fdf614c23614fda60208901613cec565b615804565b945050505050614f2f565b60e0614f7d614c23614f756fffffffffffffffffffffffffffffffff614fdf614c23614fda88613cec565b92600c8103615099575061502991926157f1565b9061503381615804565b91808311615069575090613a5a917f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c9061569b565b90507f12bacdd3000000000000000000000000000000000000000000000000000000005f5260045260245260445ffd5b600f810361511c57506150ac91926157f1565b906150b681615541565b918083106150ec575090613a5a917f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c906155ad565b90507f8b063d73000000000000000000000000000000000000000000000000000000005f5260045260245260445ffd5b600b8103615175575061513390613a5a9293615529565b1561516a57615164827f0e87e1788ebd9ed6a7e63c70a374cd3283e41cad601d21fbe27863899ed4a7085c9261565a565b9161569b565b61516482309261565a565b600e81036151a8575061519c6151926151a292613a5a9495615529565b9282949291613d00565b92615646565b916155ad565b92601084146151b957505050614ed4565b613a5a9293506151cf6151d8926151a292615529565b92919390613d00565b916151e284615541565b6149c2565b906014116103f1573560601c90565b6001600160a01b0382166001600160a01b038216105f14610ba05791565b9081602091031261032c575160ff8116810361032c5790565b60ff16604d811161039257600a0a90565b94919695968315958680156154b7575b611fa7576040517fcc56b2c50000000000000000000000000000000000000000000000000000000081526001600160a01b03938416600482015288151560248201529260209184916044918391165afa918215610fbb575f92615481575b506127106152bd6152c493836147c2565b0490613d48565b941561546a5760206001600160a01b03916004604051809481937f313ce567000000000000000000000000000000000000000000000000000000008352165afa8015610fbb576153266020916001600160a01b03935f91615453575b5061522d565b966004604051809481937f313ce567000000000000000000000000000000000000000000000000000000008352165afa8015610fbb5761536c915f91615424575061522d565b92670de0b6b3a76400008202918204670de0b6b3a76400001417156103925784615395916149b8565b90670de0b6b3a7640000810290808204670de0b6b3a7640000149015171561039257826153c1916149b8565b670de0b6b3a7640000840293808504670de0b6b3a76400001490151715610392576154198161541f93615414615403670de0b6b3a7640000996149d8996149b8565b9161540e84826159ba565b92613a16565b6159fb565b90613d48565b6147c2565b615446915060203d60201161544c575b61543e8183613792565b810190615214565b5f615320565b503d615434565b6154469150833d851161544c5761543e8183613792565b509290613d459450611f4e925061540e90826147c2565b91506020823d6020116154af575b8161549c60209383613792565b8101031261032c579051906127106152ac565b3d915061548f565b50851561524e565b916154e79183156154ea576154e0813560601c601483013560601c906151f6565b50936147d5565b91565b6154e0813560601c601583013560601c906151f6565b9160148102908082046014149015171561039257602881019182821161039257610ba093613b5c565b90606011610419578035916040602083013592013590565b61556c81307f00000000000000000000000061ff070ad105d5aa6d8f9ea21212cb574eefcad5615bba565b905f8212615578575090565b6001600160a01b03907f4c085bf1000000000000000000000000000000000000000000000000000000005f521660045260245ffd5b90918015615641576001600160a01b037f00000000000000000000000061ff070ad105d5aa6d8f9ea21212cb574eefcad516803b1561032c575f92836064926001600160a01b03948560405198899788967f0b0d9c0900000000000000000000000000000000000000000000000000000000885216600487015216602485015260448401525af18015610fbb57613ebc5750565b505050565b908161565657613d459150615541565b5090565b907f8000000000000000000000000000000000000000000000000000000000000000820361568c57613d459150615c47565b8161565657613d459150615804565b5f918315613ad4576001600160a01b037f00000000000000000000000061ff070ad105d5aa6d8f9ea21212cb574eefcad51691823b1561032c576001600160a01b03604051917fa584119400000000000000000000000000000000000000000000000000000000835216908160048201525f8160248183885af18015610fbb576157dc575b508061578f5750506020906004604051809581937f11da60b40000000000000000000000000000000000000000000000000000000083525af190811561578357506157685750565b6157809060203d602011613051576130418183613792565b50565b604051903d90823e3d90fd5b8394836020949361579f93613a3b565b6004604051809581937f11da60b40000000000000000000000000000000000000000000000000000000083525af190811561578357506157685750565b6157e99194505f90613792565b5f925f615720565b9190604011610419576020823592013590565b61582f81307f00000000000000000000000061ff070ad105d5aa6d8f9ea21212cb574eefcad5615bba565b905f82136158415750613d45906137d1565b6001600160a01b03907f3351b260000000000000000000000000000000000000000000000000000000005f521660045260245ffd5b90610140116104195780350190565b356fffffffffffffffffffffffffffffffff8116810361032c5790565b35801515810361032c5790565b91908260a091031261032c576040516158c781613749565b60806159138183956158d88161385b565b85526158e66020820161385b565b60208601526158f76040820161386f565b60408601526159086060820161387f565b60608601520161385b565b910152565b9060a0116104195780350190565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18136030182121561032c570180359067ffffffffffffffff821161032c57602001918160051b3603831361032c57565b9190811015610b0b5760051b810135907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff618136030182121561032c570190565b6149d8906159f5670de0b6b3a764000093846159ee816159e6816159de86896147c2565b0496806147c2565b0492806147c2565b0490613a16565b906147c2565b91925f935b60ff8510615a665760646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600260248201527f21790000000000000000000000000000000000000000000000000000000000006044820152fd5b615a7081856159ba565b83811015615b0f57615a828185613d48565b670de0b6b3a7640000810290808204670de0b6b3a7640000149015171561039257615ab28591611f4e8589615f65565b918215615ad1575b5050615ac890600192613a16565b945b0193615a00565b149050615b0857600181018082116103925783615aee82876159ba565b11615aff5750600183615ac8615aba565b93505092505090565b9350915050565b615b198482613d48565b670de0b6b3a7640000810290808204670de0b6b3a7640000149015171561039257615b498591611f4e8589615f65565b918215615b65575b5050615b5f90600192613d48565b94615aca565b1490508015615b7e575b615b0857600183615b5f615b51565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff810181811161039257615bb48491866159ba565b10615b6f565b6001600160a01b03809381602094165f52168252602460405f2060405194859384927ff135baaa0000000000000000000000000000000000000000000000000000000084526004840152165afa908115610fbb575f91615c18575090565b90506020813d602011615c3f575b81615c3360209383613792565b8101031261032c575190565b3d9150615c26565b6001600160a01b031680615c5a57504790565b6020602491604051928380927f70a082310000000000000000000000000000000000000000000000000000000082523060048301525afa908115610fbb575f91615c18575090565b906fffffffffffffffffffffffffffffffff8216809203615cbf57565b7f93dafdf1000000000000000000000000000000000000000000000000000000005f5260045ffd5b9092908315615e52576401000276a4905b60405194606086019386851067ffffffffffffffff861117613765576001600160a01b039788608095615dcd946020986040521515998a8152888101908a82528360408201931683526040519c8d998a997ff3cd914c000000000000000000000000000000000000000000000000000000008b528281511660048c0152828d8201511660248c015262ffffff60408201511660448c0152606081015160020b60648c0152015116608489015251151560a48801525160c4870152511660e48501526101206101048501526101248401916137fd565b03815f6001600160a01b037f00000000000000000000000061ff070ad105d5aa6d8f9ea21212cb574eefcad5165af1928315610fbb575f93615e1e575b505f1303615e1857600f0b90565b60801d90565b9092506020813d602011615e4a575b81615e3a60209383613792565b8101031261032c5751915f615e0a565b3d9150615e2d565b73fffd8963efd1fc6a506488495d951d5263988d2590615cf8565b905f6080604051615e7d81613749565b8281528260208201528260408201528260608201520152615e9d82613cec565b6001600160a01b038216916001600160a01b0382168084105f14615f3457506001600160a01b03905b1680921492602081013562ffffff811680910361032c576040820135918260020b80930361032c5760600135926001600160a01b03841680940361032c576001600160a01b039060405195615f1a87613749565b865216602085015260408401526060830152608082015291565b9150506001600160a01b038291615ec6565b5f81600f0b12615cbf576fffffffffffffffffffffffffffffffff1690565b80600302600381048203610392576159ee670de0b6b3a7640000615f99819382615f9288613d45996147c2565b04906147c2565b049282615fa682806147c2565b046147c256fea264697066735822122065b1d9e315f3e430c4c81ed52de15c02c9ca2a813345b6071ec4f52d2a9fc49664736f6c634300081d0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000494bbd8a3302aca833d307d11838f18dbada9c25000000000000000000000000fc0000000000000000000000000000000000000200000000000000000000000061ff070ad105d5aa6d8f9ea21212cb574eefcad500000000000000000000000061ff070ad105d5aa6d8f9ea21212cb574eefcad50000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000061ff070ad105d5aa6d8f9ea21212cb574eefcad500000000000000000000000031832f2a97fd20664d76cc421207669b55ce4bc000000000000000000000000004625b046c69577efc40e6c0bb83cdbafab5a55f558be7ee0c63546b31d0773eee1d90451bd76a0167bb89653722a2bd677c002d7b216153c50849f664871825fa6f22b3356cdce2436e4f48734ae2a926a4c7e5
-----Decoded View---------------
Arg [0] : params (tuple):
Arg [1] : permit2 (address): 0x494bbD8A3302AcA833D307D11838f18DbAdA9C25
Arg [2] : weth9 (address): 0xFc00000000000000000000000000000000000002
Arg [3] : v2Factory (address): 0x61fF070AD105D5aa6d8F9eA21212CB574EeFCAd5
Arg [4] : v3Factory (address): 0x61fF070AD105D5aa6d8F9eA21212CB574EeFCAd5
Arg [5] : pairInitCodeHash (bytes32): 0x0000000000000000000000000000000000000000000000000000000000000000
Arg [6] : poolInitCodeHash (bytes32): 0x0000000000000000000000000000000000000000000000000000000000000000
Arg [7] : v4PoolManager (address): 0x61fF070AD105D5aa6d8F9eA21212CB574EeFCAd5
Arg [8] : veloV2Factory (address): 0x31832f2a97Fd20664D76Cc421207669b55CE4BC0
Arg [9] : veloCLFactory (address): 0x04625B046C69577EfC40e6c0Bb83CDBAfab5a55F
Arg [10] : veloV2InitCodeHash (bytes32): 0x558be7ee0c63546b31d0773eee1d90451bd76a0167bb89653722a2bd677c002d
Arg [11] : veloCLInitCodeHash (bytes32): 0x7b216153c50849f664871825fa6f22b3356cdce2436e4f48734ae2a926a4c7e5
-----Encoded View---------------
11 Constructor Arguments found :
Arg [0] : 000000000000000000000000494bbd8a3302aca833d307d11838f18dbada9c25
Arg [1] : 000000000000000000000000fc00000000000000000000000000000000000002
Arg [2] : 00000000000000000000000061ff070ad105d5aa6d8f9ea21212cb574eefcad5
Arg [3] : 00000000000000000000000061ff070ad105d5aa6d8f9ea21212cb574eefcad5
Arg [4] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [5] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [6] : 00000000000000000000000061ff070ad105d5aa6d8f9ea21212cb574eefcad5
Arg [7] : 00000000000000000000000031832f2a97fd20664d76cc421207669b55ce4bc0
Arg [8] : 00000000000000000000000004625b046c69577efc40e6c0bb83cdbafab5a55f
Arg [9] : 558be7ee0c63546b31d0773eee1d90451bd76a0167bb89653722a2bd677c002d
Arg [10] : 7b216153c50849f664871825fa6f22b3356cdce2436e4f48734ae2a926a4c7e5
Loading...
Loading
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.