Source Code
Latest 1 internal transaction
Advanced mode:
| Parent Transaction Hash | Block | From | To | |||
|---|---|---|---|---|---|---|
| 20917183 | 239 days ago | Contract Creation | 0 FRAX |
Cross-Chain Transactions
Loading...
Loading
Contract Name:
OdosRouterV3
Compiler Version
v0.8.20+commit.a1b79de6
Optimization Enabled:
Yes with 1000 runs
Other Settings:
shanghai EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;
import "../interfaces/IOdosRouterV3.sol";
import "../interfaces/IOdosExecutor.sol";
import "../interfaces/IOdosHook.sol";
import "../interfaces/ISignatureTransfer.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/access/Ownable2Step.sol";
/// @title V3 Routing contract for Odos SOR
/// @author Transaction Assembly
/// @notice Wrapper with security gaurentees around execution of arbitrary operations on user tokens
contract OdosRouterV3 is IOdosRouterV3, Ownable2Step {
using SafeERC20 for IERC20;
/// @dev The zero address is uniquely used to represent eth since it is already
/// recognized as an invalid ERC20, and due to its gas efficiency
address constant _ETH = address(0);
/// @dev Address list where addresses can be cached for use when reading from storage is cheaper
// than reading from calldata. addressListStart is the storage slot of the first dynamic array element
uint256 private constant addressListStart =
29102676481673041902632991033461445430619272659676223336789171408008386403022;
address[] public addressList;
/// @dev Address which can access and liquidate funds held in the router
address public liquidatorAddress;
// @dev constant for the fee precision
uint256 public constant FEE_DENOM = 1e18;
constructor(address owner) Ownable(owner) { }
/// @dev Must exist in order for contract to receive eth
receive() external payable { }
/// @notice Custom decoder to swap with compact calldata for efficient execution on L2s
function swapCompact()
external
payable
returns (uint256)
{
swapTokenInfo memory tokenInfo;
swapReferralInfo memory referralInfo;
address executor;
bytes calldata pathDefinition;
{
assembly {
// Define function to load in token address, either from calldata or from storage
function getAddress(currPos) -> result, newPos {
let inputPos := shr(240, calldataload(currPos))
switch inputPos
// Reserve the null address as a special case that can be specified with 2 null bytes
case 0x0000 {
newPos := add(currPos, 2)
}
// This case means that the address is encoded in the calldata directly following the code
case 0x0001 {
result := and(shr(80, calldataload(currPos)), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)
newPos := add(currPos, 22)
}
// Otherwise we use the case to load in from the cached address list
default {
result := sload(add(addressListStart, sub(inputPos, 2)))
newPos := add(currPos, 2)
}
}
let result := 0
let pos := 4
// Load in the input and output token addresses
result, pos := getAddress(pos)
mstore(tokenInfo, result)
result, pos := getAddress(pos)
mstore(add(tokenInfo, 0x60), result)
// Load in the input amount - a 0 byte means the full balance is to be used
let inputAmountLength := shr(248, calldataload(pos))
pos := add(pos, 1)
if inputAmountLength {
mstore(add(tokenInfo, 0x20), shr(mul(sub(32, inputAmountLength), 8), calldataload(pos)))
pos := add(pos, inputAmountLength)
}
// Load in the quoted output amount
let quoteAmountLength := shr(248, calldataload(pos))
pos := add(pos, 1)
let outputQuote := shr(mul(sub(32, quoteAmountLength), 8), calldataload(pos))
mstore(add(tokenInfo, 0x80), outputQuote)
pos := add(pos, quoteAmountLength)
// Load the slippage tolerance and use to get the minimum output amount
{
let slippageTolerance := shr(232, calldataload(pos))
mstore(add(tokenInfo, 0xA0), div(mul(outputQuote, sub(0xFFFFFF, slippageTolerance)), 0xFFFFFF))
}
pos := add(pos, 3)
// Load in the executor address
executor, pos := getAddress(pos)
// Load in the destination to send the input to - Zero denotes the executor
result, pos := getAddress(pos)
if eq(result, 0) { result := executor }
mstore(add(tokenInfo, 0x40), result)
// Load in the destination to send the output to - Zero denotes msg.sender
result, pos := getAddress(pos)
mstore(add(tokenInfo, 0xC0), result)
let referralCode := shr(192, calldataload(pos))
pos := add(pos, 8)
mstore(referralInfo, referralCode)
let feeStatus := shr(248, calldataload(pos))
pos := add(pos, 1)
if feeStatus {
let referralFee := shr(192, calldataload(pos))
pos := add(pos, 8)
mstore(add(referralInfo, 0x20), referralFee)
let referralBeneficiary := shr(96, calldataload(pos))
pos := add(pos, 20)
mstore(add(referralInfo, 0x40), referralBeneficiary)
}
// Set the offset and size for the pathDefinition portion of the msg.data
pathDefinition.length := mul(shr(248, calldataload(pos)), 32)
pathDefinition.offset := add(pos, 1)
}
}
return _swapApproval(
tokenInfo,
pathDefinition,
executor,
referralInfo
);
}
/// @notice Externally facing interface for swapping two tokens
/// @param tokenInfo All information about the tokens being swapped
/// @param pathDefinition Encoded path definition for executor
/// @param executor Address of contract that will execute the path
/// @param referralInfo referral info to specify the source of and fee for the swap
function swap(
swapTokenInfo memory tokenInfo,
bytes calldata pathDefinition,
address executor,
swapReferralInfo memory referralInfo
)
external
payable
returns (uint256 amountOut)
{
return _swapApproval(
tokenInfo,
pathDefinition,
executor,
referralInfo
);
}
/// @notice Externally facing interface for swapping two tokens with a call at the end
/// @param tokenInfo All information about the tokens being swapped
/// @param pathDefinition Encoded path definition for executor
/// @param executor Address of contract that will execute the path
/// @param referralInfo referral info to specify the source of and fee for the swap
/// @param hookTarget the target address to call the hook on
/// @param hookData encoded data for a call to the hookTarget after the swap
function swapWithHook(
swapTokenInfo memory tokenInfo,
bytes calldata pathDefinition,
address executor,
swapReferralInfo memory referralInfo,
address hookTarget,
bytes calldata hookData
)
external
payable
returns (uint256 amountOut)
{
amountOut = _swapApproval(
tokenInfo,
pathDefinition,
executor,
referralInfo
);
uint256[] memory hookAmountsIn = new uint256[](1);
hookAmountsIn[0] = amountOut;
IOdosHook(hookTarget).executeOdosHook(
hookData,
hookAmountsIn,
msg.sender
);
}
/// @notice Internal function for initiating approval transfers
/// @param tokenInfo All information about the tokens being swapped
/// @param pathDefinition Encoded path definition for executor
/// @param executor Address of contract that will execute the path
/// @param referralInfo referral info to specify the source of and fee for the swap
function _swapApproval(
swapTokenInfo memory tokenInfo,
bytes calldata pathDefinition,
address executor,
swapReferralInfo memory referralInfo
)
internal
returns (uint256 amountOut)
{
if (tokenInfo.inputToken == _ETH) {
// Support rebasing tokens by allowing the user to trade the entire balance
if (tokenInfo.inputAmount == 0) {
tokenInfo.inputAmount = msg.value;
} else {
require(msg.value == tokenInfo.inputAmount, "Wrong msg.value");
}
}
else {
require(msg.value == 0, "Wrong msg.value");
// Support rebasing tokens by allowing the user to trade the entire balance
if (tokenInfo.inputAmount == 0) {
tokenInfo.inputAmount = IERC20(tokenInfo.inputToken).balanceOf(msg.sender);
}
IERC20(tokenInfo.inputToken).safeTransferFrom(
msg.sender,
tokenInfo.inputReceiver,
tokenInfo.inputAmount
);
}
return _swap(
tokenInfo,
pathDefinition,
executor,
referralInfo
);
}
/// @notice Externally facing interface for swapping two tokens
/// @param permit2 All additional info for Permit2 transfers
/// @param tokenInfo All information about the tokens being swapped
/// @param pathDefinition Encoded path definition for executor
/// @param executor Address of contract that will execute the path
/// @param referralInfo referral info to specify the source of and fee for the swap
function swapPermit2(
permit2Info memory permit2,
swapTokenInfo memory tokenInfo,
bytes calldata pathDefinition,
address executor,
swapReferralInfo memory referralInfo
)
external
returns (uint256 amountOut)
{
return _swapPermit2(
permit2,
tokenInfo,
pathDefinition,
executor,
referralInfo
);
}
/// @notice Externally facing interface for swapping two tokens
/// @param permit2 All additional info for Permit2 transfers
/// @param tokenInfo All information about the tokens being swapped
/// @param pathDefinition Encoded path definition for executor
/// @param executor Address of contract that will execute the path
/// @param referralInfo referral info to specify the source of and fee for the swap
/// @param hookTarget the target address to call the hook on
/// @param hookData encoded data for a call to the hookTarget after the swap
function swapPermit2WithHook(
permit2Info memory permit2,
swapTokenInfo memory tokenInfo,
bytes calldata pathDefinition,
address executor,
swapReferralInfo memory referralInfo,
address hookTarget,
bytes calldata hookData
)
external
returns (uint256 amountOut)
{
amountOut = _swapPermit2(
permit2,
tokenInfo,
pathDefinition,
executor,
referralInfo
);
uint256[] memory hookAmountsIn = new uint256[](1);
hookAmountsIn[0] = amountOut;
IOdosHook(hookTarget).executeOdosHook(
hookData,
hookAmountsIn,
msg.sender
);
}
/// @notice Internal function for using permit2 before a swap
/// @param permit2 All additional info for Permit2 transfers
/// @param tokenInfo All information about the tokens being swapped
/// @param pathDefinition Encoded path definition for executor
/// @param executor Address of contract that will execute the path
/// @param referralInfo referral info to specify the source of and fee for the swap
function _swapPermit2(
permit2Info memory permit2,
swapTokenInfo memory tokenInfo,
bytes calldata pathDefinition,
address executor,
swapReferralInfo memory referralInfo
)
internal
returns (uint256 amountOut)
{
ISignatureTransfer(permit2.contractAddress).permitTransferFrom(
ISignatureTransfer.PermitTransferFrom(
ISignatureTransfer.TokenPermissions(
tokenInfo.inputToken,
tokenInfo.inputAmount
),
permit2.nonce,
permit2.deadline
),
ISignatureTransfer.SignatureTransferDetails(
tokenInfo.inputReceiver,
tokenInfo.inputAmount
),
msg.sender,
permit2.signature
);
return _swap(
tokenInfo,
pathDefinition,
executor,
referralInfo
);
}
/// @notice contains the main logic for swapping one token for another
/// Assumes input tokens have already been sent to their destinations and
/// that msg.value is set to expected ETH input value, or 0 for ERC20 input
/// @param tokenInfo All information about the tokens being swapped
/// @param pathDefinition Encoded path definition for executor
/// @param executor Address of contract that will execute the path
/// @param referralInfo referral info to specify the source of and fee for the swap
function _swap(
swapTokenInfo memory tokenInfo,
bytes calldata pathDefinition,
address executor,
swapReferralInfo memory referralInfo
)
internal
returns (uint256 amountOut)
{
// Check for valid output specifications
require(tokenInfo.outputMin <= tokenInfo.outputQuote, "Minimum greater than quote");
require(tokenInfo.outputMin > 0, "Minimum output is zero");
require(tokenInfo.inputToken != tokenInfo.outputToken, "Arbitrage not supported");
uint256 balanceBefore = _universalBalance(tokenInfo.outputToken);
// Delegate the execution of the path to the specified Odos Executor
uint256[] memory amountsIn = new uint256[](1);
amountsIn[0] = tokenInfo.inputAmount;
IOdosExecutor(executor).executePath{value: msg.value}(pathDefinition, amountsIn, msg.sender);
amountOut = _universalBalance(tokenInfo.outputToken) - balanceBefore;
if (referralInfo.fee > 0) {
require(referralInfo.feeRecipient != address(0), "Null fee recipient");
require(referralInfo.fee <= FEE_DENOM / 50, "Fee too high");
uint256 splitBPS = (referralInfo.code >> 32) & 65535;
if (splitBPS == 0) splitBPS = 8000;
require(splitBPS <= 10000, "Invalid Ref Code");
if (referralInfo.feeRecipient != address(this)) {
_universalTransfer(
tokenInfo.outputToken,
referralInfo.feeRecipient,
amountOut * referralInfo.fee * splitBPS / (FEE_DENOM * 10000)
);
}
amountOut = amountOut * (FEE_DENOM - referralInfo.fee) / FEE_DENOM;
}
int256 slippage = int256(amountOut) - int256(tokenInfo.outputQuote);
if (slippage > 0 && (referralInfo.code >> 48) & 1 == 0) {
amountOut = tokenInfo.outputQuote;
}
require(amountOut >= tokenInfo.outputMin, "Slippage Limit Exceeded");
// Transfer out the final output to the end user
_universalTransfer(
tokenInfo.outputToken,
tokenInfo.outputReceiver == address(0) ? msg.sender : tokenInfo.outputReceiver,
amountOut
);
emit Swap(
msg.sender,
tokenInfo.inputAmount,
tokenInfo.inputToken,
amountOut,
tokenInfo.outputToken,
slippage,
referralInfo.code,
referralInfo.fee,
referralInfo.feeRecipient
);
}
/// @notice Custom decoder to swapMulti with compact calldata for efficient execution on L2s
function swapMultiCompact()
external
payable
returns (uint256[] memory amountsOut)
{
address executor;
inputTokenInfo[] memory inputs;
outputTokenInfo[] memory outputs;
uint256 pos = 6;
{
uint256 numInputs;
uint256 numOutputs;
assembly {
numInputs := shr(248, calldataload(4))
numOutputs := shr(248, calldataload(5))
}
inputs = new inputTokenInfo[](numInputs);
outputs = new outputTokenInfo[](numOutputs);
assembly {
// Define function to load in token address, either from calldata or from storage
function getAddress(currPos) -> result, newPos {
let inputPos := shr(240, calldataload(currPos))
switch inputPos
// Reserve the null address as a special case that can be specified with 2 null bytes
case 0x0000 {
newPos := add(currPos, 2)
}
// This case means that the address is encoded in the calldata directly following the code
case 0x0001 {
result := and(shr(80, calldataload(currPos)), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)
newPos := add(currPos, 22)
}
// Otherwise we use the case to load in from the cached address list
default {
result := sload(add(addressListStart, sub(inputPos, 2)))
newPos := add(currPos, 2)
}
}
executor, pos := getAddress(pos)
let slippageTolerance := shr(232, calldataload(pos))
pos := add(pos, 3)
let result := 0
let memPos := 0
for { let element := 0 } lt(element, numInputs) { element := add(element, 1) }
{
memPos := mload(add(inputs, add(mul(element, 0x20), 0x20)))
// Load in the token address
result, pos := getAddress(pos)
mstore(memPos, result)
// Load in the input amount - a 0 byte means the full balance is to be used
let inputAmountLength := shr(248, calldataload(pos))
pos := add(pos, 1)
if inputAmountLength {
mstore(add(memPos, 0x20), shr(mul(sub(32, inputAmountLength), 8), calldataload(pos)))
pos := add(pos, inputAmountLength)
}
result, pos := getAddress(pos)
if eq(result, 0) { result := executor }
mstore(add(memPos, 0x40), result)
}
for { let element := 0 } lt(element, numOutputs) { element := add(element, 1) }
{
memPos := mload(add(outputs, add(mul(element, 0x20), 0x20)))
// Load in the token address
result, pos := getAddress(pos)
mstore(memPos, result)
// Load in the quoted output amount
let outputAmountLength := shr(248, calldataload(pos))
pos := add(pos, 1)
let outputQuote := shr(mul(sub(32, outputAmountLength), 8), calldataload(pos))
mstore(add(memPos, 0x20), outputQuote)
pos := add(pos, outputAmountLength)
// Set the minimum output amount as quote with slippage limit applied
mstore(add(memPos, 0x40), div(mul(outputQuote, sub(0xFFFFFF, slippageTolerance)), 0xFFFFFF))
result, pos := getAddress(pos)
mstore(add(memPos, 0x60), result)
}
}
}
swapReferralInfo memory referralInfo;
bytes calldata pathDefinition;
assembly {
let referralCode := shr(192, calldataload(pos))
pos := add(pos, 8)
mstore(referralInfo, referralCode)
let feeStatus := shr(248, calldataload(pos))
pos := add(pos, 1)
if feeStatus {
let referralFee := shr(192, calldataload(pos))
pos := add(pos, 8)
mstore(add(referralInfo, 0x20), referralFee)
let referralBeneficiary := shr(96, calldataload(pos))
pos := add(pos, 20)
mstore(add(referralInfo, 0x40), referralBeneficiary)
}
// Set the offset and size for the pathDefinition portion of the msg.data
pathDefinition.length := mul(shr(248, calldataload(pos)), 32)
pathDefinition.offset := add(pos, 1)
}
return _swapMultiApproval(
inputs,
outputs,
pathDefinition,
executor,
referralInfo
);
}
/// @notice Externally facing interface for swapping between two sets of tokens
/// @param inputs list of input token structs for the path being executed
/// @param outputs list of output token structs for the path being executed
/// @param pathDefinition Encoded path definition for executor
/// @param executor Address of contract that will execute the path
/// @param referralInfo referral info to specify the source of and fee for the swap
function swapMulti(
inputTokenInfo[] memory inputs,
outputTokenInfo[] memory outputs,
bytes calldata pathDefinition,
address executor,
swapReferralInfo memory referralInfo
)
external
payable
returns (uint256[] memory amountsOut)
{
return _swapMultiApproval(
inputs,
outputs,
pathDefinition,
executor,
referralInfo
);
}
/// @notice Externally facing interface for swapping between two sets of tokens with a hook
/// @param inputs list of input token structs for the path being executed
/// @param outputs list of output token structs for the path being executed
/// @param pathDefinition Encoded path definition for executor
/// @param executor Address of contract that will execute the path
/// @param referralInfo referral info to specify the source of and fee for the swap
/// @param hookTarget the target address to call the hook on
/// @param hookData encoded data for a call to the hookTarget after the swap
function swapMultiWithHook(
inputTokenInfo[] memory inputs,
outputTokenInfo[] memory outputs,
bytes calldata pathDefinition,
address executor,
swapReferralInfo memory referralInfo,
address hookTarget,
bytes calldata hookData
)
external
payable
returns (uint256[] memory amountsOut)
{
amountsOut = _swapMultiApproval(
inputs,
outputs,
pathDefinition,
executor,
referralInfo
);
IOdosHook(hookTarget).executeOdosHook(
hookData,
amountsOut,
msg.sender
);
}
/// @notice Internal logic for swapping between two sets of tokens with approvals
/// @param inputs list of input token structs for the path being executed
/// @param outputs list of output token structs for the path being executed
/// @param pathDefinition Encoded path definition for executor
/// @param executor Address of contract that will execute the path
/// @param referralInfo referral info to specify the source of and fee for the swap
function _swapMultiApproval(
inputTokenInfo[] memory inputs,
outputTokenInfo[] memory outputs,
bytes calldata pathDefinition,
address executor,
swapReferralInfo memory referralInfo
)
internal
returns (uint256[] memory amountsOut)
{
// If input amount is still 0 then that means the maximum possible input is to be used
uint256 expected_msg_value = 0;
for (uint256 i = 0; i < inputs.length; i++) {
if (inputs[i].tokenAddress == _ETH) {
if (inputs[i].amountIn == 0) {
inputs[i].amountIn = msg.value;
}
expected_msg_value = inputs[i].amountIn;
}
else {
if (inputs[i].amountIn == 0) {
inputs[i].amountIn = IERC20(inputs[i].tokenAddress).balanceOf(msg.sender);
}
IERC20(inputs[i].tokenAddress).safeTransferFrom(
msg.sender,
inputs[i].receiver,
inputs[i].amountIn
);
}
}
require(msg.value == expected_msg_value, "Wrong msg.value");
return _swapMulti(
inputs,
outputs,
pathDefinition,
executor,
referralInfo
);
}
/// @notice Externally facing function for swapping between two sets of tokens with Permit2
/// @param permit2 All additional info for Permit2 transfers
/// @param inputs list of input token structs for the path being executed
/// @param outputs list of output token structs for the path being executed
/// @param pathDefinition Encoded path definition for executor
/// @param executor Address of contract that will execute the path
/// @param referralInfo referral info to specify the source of and fee for the swap
function swapMultiPermit2(
permit2Info memory permit2,
inputTokenInfo[] memory inputs,
outputTokenInfo[] memory outputs,
bytes calldata pathDefinition,
address executor,
swapReferralInfo memory referralInfo
)
external
payable
returns (uint256[] memory amountsOut)
{
return _swapMultiPermit2(
permit2,
inputs,
outputs,
pathDefinition,
executor,
referralInfo
);
}
/// @notice Externally facing function for swapping between two sets of tokens with Permit2 with a hook
/// @param permit2 All additional info for Permit2 transfers
/// @param inputs list of input token structs for the path being executed
/// @param outputs list of output token structs for the path being executed
/// @param pathDefinition Encoded path definition for executor
/// @param executor Address of contract that will execute the path
/// @param referralInfo referral info to specify the source of and fee for the swap
/// @param hookTarget the target address to call the hook on
/// @param hookData encoded data for a call to the hookTarget after the swap
function swapMultiPermit2WithHook(
permit2Info memory permit2,
inputTokenInfo[] memory inputs,
outputTokenInfo[] memory outputs,
bytes calldata pathDefinition,
address executor,
swapReferralInfo memory referralInfo,
address hookTarget,
bytes calldata hookData
)
external
payable
returns (uint256[] memory amountsOut)
{
amountsOut = _swapMultiPermit2(
permit2,
inputs,
outputs,
pathDefinition,
executor,
referralInfo
);
IOdosHook(hookTarget).executeOdosHook(
hookData,
amountsOut,
msg.sender
);
}
/// @notice Internal function for approcing with premit2 before swapping multiple tokens
/// @param permit2 All additional info for Permit2 transfers
/// @param inputs list of input token structs for the path being executed
/// @param outputs list of output token structs for the path being executed
/// @param pathDefinition Encoded path definition for executor
/// @param executor Address of contract that will execute the path
/// @param referralInfo referral info to specify the source of and fee for the swap
function _swapMultiPermit2(
permit2Info memory permit2,
inputTokenInfo[] memory inputs,
outputTokenInfo[] memory outputs,
bytes calldata pathDefinition,
address executor,
swapReferralInfo memory referralInfo
)
internal
returns (uint256[] memory amountsOut)
{
ISignatureTransfer.PermitBatchTransferFrom memory permit;
ISignatureTransfer.SignatureTransferDetails[] memory transferDetails;
{
uint256 permit_length = msg.value > 0 ? inputs.length - 1 : inputs.length;
permit = ISignatureTransfer.PermitBatchTransferFrom(
new ISignatureTransfer.TokenPermissions[](permit_length),
permit2.nonce,
permit2.deadline
);
transferDetails =
new ISignatureTransfer.SignatureTransferDetails[](permit_length);
}
{
uint256 expected_msg_value = 0;
for (uint256 i = 0; i < inputs.length; i++) {
if (inputs[i].tokenAddress == _ETH) {
if (inputs[i].amountIn == 0) {
inputs[i].amountIn = msg.value;
}
expected_msg_value = inputs[i].amountIn;
}
else {
if (inputs[i].amountIn == 0) {
inputs[i].amountIn = IERC20(inputs[i].tokenAddress).balanceOf(msg.sender);
}
uint256 permit_index = expected_msg_value == 0 ? i : i - 1;
permit.permitted[permit_index].token = inputs[i].tokenAddress;
permit.permitted[permit_index].amount = inputs[i].amountIn;
transferDetails[permit_index].to = inputs[i].receiver;
transferDetails[permit_index].requestedAmount = inputs[i].amountIn;
}
}
require(msg.value == expected_msg_value, "Wrong msg.value");
}
ISignatureTransfer(permit2.contractAddress).permitTransferFrom(
permit,
transferDetails,
msg.sender,
permit2.signature
);
return _swapMulti(
inputs,
outputs,
pathDefinition,
executor,
referralInfo
);
}
/// @notice contains the main logic for swapping between two sets of tokens
/// assumes that inputs have already been sent to the right location and msg.value
/// is set correctly to be 0 for no native input and match native inpuit otherwise
/// @param inputs list of input token structs for the path being executed
/// @param outputs list of output token structs for the path being executed
/// @param pathDefinition Encoded path definition for executor
/// @param executor Address of contract that will execute the path
/// @param referralInfo referral info to specify the source of and fee for the swap
function _swapMulti(
inputTokenInfo[] memory inputs,
outputTokenInfo[] memory outputs,
bytes calldata pathDefinition,
address executor,
swapReferralInfo memory referralInfo
)
internal
returns (uint256[] memory amountsOut)
{
// Extract arrays of input amount values and tokens from the inputs struct list
uint256[] memory amountsIn = new uint256[](inputs.length);
address[] memory tokensIn = new address[](inputs.length);
// Check input specification validity and transfer input tokens to executor
{
for (uint256 i = 0; i < inputs.length; i++) {
amountsIn[i] = inputs[i].amountIn;
tokensIn[i] = inputs[i].tokenAddress;
for (uint256 j = 0; j < i; j++) {
require(
inputs[i].tokenAddress != inputs[j].tokenAddress,
"Duplicate source tokens"
);
}
for (uint256 j = 0; j < outputs.length; j++) {
require(
inputs[i].tokenAddress != outputs[j].tokenAddress,
"Arbitrage not supported"
);
}
}
}
// Check outputs for duplicates and record balances before swap
uint256[] memory balancesBefore = new uint256[](outputs.length);
for (uint256 i = 0; i < outputs.length; i++) {
require(
outputs[i].amountMin <= outputs[i].amountQuote,
"Minimum greater than quote"
);
require(
outputs[i].amountMin > 0,
"Minimum output is zero"
);
for (uint256 j = 0; j < i; j++) {
require(
outputs[i].tokenAddress != outputs[j].tokenAddress,
"Duplicate destination tokens"
);
}
balancesBefore[i] = _universalBalance(outputs[i].tokenAddress);
}
// Delegate the execution of the path to the specified Odos Executor
IOdosExecutor(executor).executePath{value: msg.value}(pathDefinition, amountsIn, msg.sender);
int256[] memory slippage = new int256[](outputs.length);
{
amountsOut = new uint256[](outputs.length);
uint256 splitBPS = (referralInfo.code >> 32) & 65535;
if (splitBPS == 0) splitBPS = 8000;
require(splitBPS <= 10000, "Invalid Ref Code");
for (uint256 i = 0; i < outputs.length; i++) {
// Record the destination token balance before the path is executed
amountsOut[i] = _universalBalance(outputs[i].tokenAddress) - balancesBefore[i];
if (referralInfo.fee > 0) {
require(referralInfo.feeRecipient != address(0), "Null fee recipient");
require(referralInfo.fee <= FEE_DENOM / 50, "Fee too high");
if (referralInfo.feeRecipient != address(this)) {
_universalTransfer(
outputs[i].tokenAddress,
referralInfo.feeRecipient,
amountsOut[i] * referralInfo.fee * splitBPS / (FEE_DENOM * 10000)
);
}
amountsOut[i] = amountsOut[i] * (FEE_DENOM - referralInfo.fee) / FEE_DENOM;
}
slippage[i] = int256(amountsOut[i]) - int256(outputs[i].amountQuote);
if (slippage[i] > 0 && (referralInfo.code >> 48) & 1 == 0) {
amountsOut[i] = outputs[i].amountQuote;
}
require(amountsOut[i] >= outputs[i].amountMin, "Slippage Limit Exceeded");
_universalTransfer(
outputs[i].tokenAddress,
outputs[i].receiver == address(0) ? msg.sender : outputs[i].receiver,
amountsOut[i]
);
}
}
address[] memory tokensOut = new address[](outputs.length);
for (uint256 i = 0; i < outputs.length; i++) {
tokensOut[i] = outputs[i].tokenAddress;
}
emit SwapMulti(
msg.sender,
amountsIn,
tokensIn,
amountsOut,
tokensOut,
slippage,
referralInfo.code,
referralInfo.fee,
referralInfo.feeRecipient
);
}
/// @notice Changes the liquidator address
/// @param account The address of new liquidator
function changeLiquidatorAddress(address account)
external
onlyOwner
{
liquidatorAddress = account;
emit LiquidatorAddressChanged(account);
}
/// @notice Push new addresses to the cached address list for when storage is cheaper than calldata
/// @param addresses list of addresses to be added to the cached address list
function writeAddressList(
address[] calldata addresses
)
external
onlyOwner
{
for (uint256 i = 0; i < addresses.length; i++) {
addressList.push(addresses[i]);
}
}
/// @notice Allows the owner to transfer funds held by the router contract
/// @param tokens List of token address to be transferred
/// @param amounts List of amounts of each token to be transferred
/// @param dest Address to which the funds should be sent
function transferRouterFunds(
address[] calldata tokens,
uint256[] calldata amounts,
address dest
)
external
{
require(msg.sender == liquidatorAddress || msg.sender == owner(), "Address not allowed");
require(tokens.length == amounts.length, "Invalid funds transfer");
for (uint256 i = 0; i < tokens.length; i++) {
_universalTransfer(
tokens[i],
dest,
amounts[i] == 0 ? _universalBalance(tokens[i]) : amounts[i]
);
}
}
/// @notice Directly swap funds held in router
/// @param inputs list of input token structs for the path being executed
/// @param outputs list of output token structs for the path being executed
/// @param pathDefinition Encoded path definition for executor
/// @param executor Address of contract that will execute the path
function swapRouterFunds(
inputTokenInfo[] memory inputs,
outputTokenInfo[] memory outputs,
bytes calldata pathDefinition,
address executor
)
external
returns (uint256[] memory amountsOut)
{
require(msg.sender == liquidatorAddress || msg.sender == owner(), "Address not allowed");
uint256[] memory amountsIn = new uint256[](inputs.length);
address[] memory tokensIn = new address[](inputs.length);
for (uint256 i = 0; i < inputs.length; i++) {
tokensIn[i] = inputs[i].tokenAddress;
amountsIn[i] = inputs[i].amountIn == 0 ?
_universalBalance(tokensIn[i]) : inputs[i].amountIn;
_universalTransfer(
tokensIn[i],
inputs[i].receiver,
amountsIn[i]
);
}
// Check outputs for duplicates and record balances before swap
uint256[] memory balancesBefore = new uint256[](outputs.length);
address[] memory tokensOut = new address[](outputs.length);
for (uint256 i = 0; i < outputs.length; i++) {
tokensOut[i] = outputs[i].tokenAddress;
balancesBefore[i] = _universalBalance(tokensOut[i]);
}
// Delegate the execution of the path to the specified Odos Executor
IOdosExecutor(executor).executePath{value: 0}(pathDefinition, amountsIn, msg.sender);
amountsOut = new uint256[](outputs.length);
for (uint256 i = 0; i < outputs.length; i++) {
// Record the destination token balance before the path is executed
amountsOut[i] = _universalBalance(tokensOut[i]) - balancesBefore[i];
require(amountsOut[i] >= outputs[i].amountMin, "Slippage Limit Exceeded");
_universalTransfer(
outputs[i].tokenAddress,
outputs[i].receiver == address(0) ? msg.sender : outputs[i].receiver,
amountsOut[i]
);
}
emit SwapMulti(
msg.sender,
amountsIn,
tokensIn,
amountsOut,
tokensOut,
new int256[](outputs.length),
0,
0,
address(0)
);
}
/// @notice helper function to get balance of ERC20 or native coin for this contract
/// @param token address of the token to check, null for native coin
/// @return balance of specified coin or token
function _universalBalance(address token) private view returns(uint256) {
if (token == _ETH) {
return address(this).balance;
} else {
return IERC20(token).balanceOf(address(this));
}
}
/// @notice helper function to transfer ERC20 or native coin
/// @param token address of the token being transferred, null for native coin
/// @param to address to transfer to
/// @param amount to transfer
function _universalTransfer(address token, address to, uint256 amount) private {
if (token == _ETH) {
(bool success,) = payable(to).call{value: amount}("");
require(success, "ETH transfer failed");
} else {
IERC20(token).safeTransfer(to, amount);
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
/// @title V3 Routing contract interface for Odos SOR
/// @author Transaction Assembly
/// @notice Wrapper with security gaurentees around execution of arbitrary operations on user tokens
interface IOdosRouterV3 {
/// @dev Contains all information needed to describe the input and output for a swap
struct permit2Info {
address contractAddress;
uint256 nonce;
uint256 deadline;
bytes signature;
}
/// @dev Contains all information needed to describe the input and output for a swap
struct swapTokenInfo {
address inputToken;
uint256 inputAmount;
address inputReceiver;
address outputToken;
uint256 outputQuote;
uint256 outputMin;
address outputReceiver;
}
/// @dev Contains all information needed to describe an intput token for swapMulti
struct inputTokenInfo {
address tokenAddress;
uint256 amountIn;
address receiver;
}
/// @dev Contains all information needed to describe an output token for swapMulti
struct outputTokenInfo {
address tokenAddress;
uint256 amountQuote;
uint256 amountMin;
address receiver;
}
/// @dev Holds all information for a given referral
struct swapReferralInfo {
uint64 code;
uint64 fee;
address feeRecipient;
}
/// @dev Event emitted on changing the liquidator address
event LiquidatorAddressChanged(address indexed account);
// @dev event for swapping one token for another
event Swap(
address sender,
uint256 inputAmount,
address inputToken,
uint256 amountOut,
address outputToken,
int256 slippage,
uint64 referralCode,
uint64 referralFee,
address referralFeeRecipient
);
/// @dev event for swapping multiple input and/or output tokens
event SwapMulti(
address sender,
uint256[] amountsIn,
address[] tokensIn,
uint256[] amountsOut,
address[] tokensOut,
int256[] slippage,
uint64 referralCode,
uint64 referralFee,
address referralFeeRecipient
);
function swapCompact() external payable returns (uint256);
function swap(
swapTokenInfo memory tokenInfo,
bytes calldata pathDefinition,
address executor,
swapReferralInfo memory referralInfo
)
external payable returns (uint256 amountOut);
function swapPermit2(
permit2Info memory permit2,
swapTokenInfo memory tokenInfo,
bytes calldata pathDefinition,
address executor,
swapReferralInfo memory referralInfo
)
external returns (uint256 amountOut);
function swapMultiCompact() external payable returns (uint256[] memory amountsOut);
function swapMulti(
inputTokenInfo[] memory inputs,
outputTokenInfo[] memory outputs,
bytes calldata pathDefinition,
address executor,
swapReferralInfo memory referralInfo
)
external payable returns (uint256[] memory amountsOut);
function swapMultiPermit2(
permit2Info memory permit2,
inputTokenInfo[] memory inputs,
outputTokenInfo[] memory outputs,
bytes calldata pathDefinition,
address executor,
swapReferralInfo memory referralInfo
)
external payable returns (uint256[] memory amountsOut);
function changeLiquidatorAddress(address account)
external;
function writeAddressList(
address[] calldata addresses
)
external;
function transferRouterFunds(
address[] calldata tokens,
uint256[] calldata amounts,
address dest
)
external;
function swapRouterFunds(
inputTokenInfo[] memory inputs,
outputTokenInfo[] memory outputs,
bytes calldata pathDefinition,
address executor
)
external
returns (uint256[] memory amountsOut);
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;
interface IOdosExecutor {
function executePath (
bytes calldata bytecode,
uint256[] memory inputAmount,
address msgSender
) external payable;
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;
interface IOdosHook {
function executeOdosHook (
bytes calldata hookData,
uint256[] memory inputAmounts,
address msgSender
) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
/// @title SignatureTransfer
/// @notice Handles ERC20 token transfers through signature based actions
/// @dev Requires user's token approval on the Permit2 contract
interface ISignatureTransfer {
/// @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
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC-20 standard as defined in the ERC.
*/
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
// OpenZeppelin Contracts (last updated v5.3.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
import {IERC1363} from "../../../interfaces/IERC1363.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC-20 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 {
/**
* @dev An operation with an ERC-20 token failed.
*/
error SafeERC20FailedOperation(address token);
/**
* @dev Indicates a failed `decreaseAllowance` request.
*/
error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
/**
* @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.encodeCall(token.transfer, (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.encodeCall(token.transferFrom, (from, to, value)));
}
/**
* @dev Variant of {safeTransfer} that returns a bool instead of reverting if the operation is not successful.
*/
function trySafeTransfer(IERC20 token, address to, uint256 value) internal returns (bool) {
return _callOptionalReturnBool(token, abi.encodeCall(token.transfer, (to, value)));
}
/**
* @dev Variant of {safeTransferFrom} that returns a bool instead of reverting if the operation is not successful.
*/
function trySafeTransferFrom(IERC20 token, address from, address to, uint256 value) internal returns (bool) {
return _callOptionalReturnBool(token, abi.encodeCall(token.transferFrom, (from, to, 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.
*
* IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
* smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
* this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
* that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
*/
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
forceApprove(token, spender, oldAllowance + value);
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
* value, non-reverting calls are assumed to be successful.
*
* IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
* smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
* this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
* that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
*/
function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
unchecked {
uint256 currentAllowance = token.allowance(address(this), spender);
if (currentAllowance < requestedDecrease) {
revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
}
forceApprove(token, spender, currentAllowance - requestedDecrease);
}
}
/**
* @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.
*
* NOTE: If the token implements ERC-7674, this function will not modify any temporary allowance. This function
* only sets the "standard" allowance. Any temporary allowance will remain active, in addition to the value being
* set here.
*/
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Performs an {ERC1363} transferAndCall, with a fallback to the simple {ERC20} transfer if the target has no
* code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* Reverts if the returned value is other than `true`.
*/
function transferAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
if (to.code.length == 0) {
safeTransfer(token, to, value);
} else if (!token.transferAndCall(to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Performs an {ERC1363} transferFromAndCall, with a fallback to the simple {ERC20} transferFrom if the target
* has no code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* Reverts if the returned value is other than `true`.
*/
function transferFromAndCallRelaxed(
IERC1363 token,
address from,
address to,
uint256 value,
bytes memory data
) internal {
if (to.code.length == 0) {
safeTransferFrom(token, from, to, value);
} else if (!token.transferFromAndCall(from, to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Performs an {ERC1363} approveAndCall, with a fallback to the simple {ERC20} approve if the target has no
* code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* NOTE: When the recipient address (`to`) has no code (i.e. is an EOA), this function behaves as {forceApprove}.
* Opposedly, when the recipient address (`to`) has code, this function only attempts to call {ERC1363-approveAndCall}
* once without retrying, and relies on the returned value to be true.
*
* Reverts if the returned value is other than `true`.
*/
function approveAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
if (to.code.length == 0) {
forceApprove(token, to, value);
} else if (!token.approveAndCall(to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @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 {_callOptionalReturnBool} that reverts if call fails to meet the requirements.
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
uint256 returnSize;
uint256 returnValue;
assembly ("memory-safe") {
let success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
// bubble errors
if iszero(success) {
let ptr := mload(0x40)
returndatacopy(ptr, 0, returndatasize())
revert(ptr, returndatasize())
}
returnSize := returndatasize()
returnValue := mload(0)
}
if (returnSize == 0 ? address(token).code.length == 0 : returnValue != 1) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @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 silently catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
bool success;
uint256 returnSize;
uint256 returnValue;
assembly ("memory-safe") {
success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
returnSize := returndatasize()
returnValue := mload(0)
}
return success && (returnSize == 0 ? address(token).code.length > 0 : returnValue == 1);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (access/Ownable2Step.sol)
pragma solidity ^0.8.20;
import {Ownable} from "./Ownable.sol";
/**
* @dev Contract module which provides access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* This extension of the {Ownable} contract includes a two-step mechanism to transfer
* ownership, where the new owner must call {acceptOwnership} in order to replace the
* old one. This can help prevent common mistakes, such as transfers of ownership to
* incorrect accounts, or to contracts that are unable to interact with the
* permission system.
*
* The initial owner is specified at deployment time in the constructor for `Ownable`. This
* can later be changed with {transferOwnership} and {acceptOwnership}.
*
* This module is used through inheritance. It will make available all functions
* from parent (Ownable).
*/
abstract contract Ownable2Step is Ownable {
address private _pendingOwner;
event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);
/**
* @dev Returns the address of the pending owner.
*/
function pendingOwner() public view virtual returns (address) {
return _pendingOwner;
}
/**
* @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.
* Can only be called by the current owner.
*
* Setting `newOwner` to the zero address is allowed; this can be used to cancel an initiated ownership transfer.
*/
function transferOwnership(address newOwner) public virtual override onlyOwner {
_pendingOwner = newOwner;
emit OwnershipTransferStarted(owner(), newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual override {
delete _pendingOwner;
super._transferOwnership(newOwner);
}
/**
* @dev The new owner accepts the ownership transfer.
*/
function acceptOwnership() public virtual {
address sender = _msgSender();
if (pendingOwner() != sender) {
revert OwnableUnauthorizedAccount(sender);
}
_transferOwnership(sender);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (interfaces/IERC1363.sol)
pragma solidity ^0.8.20;
import {IERC20} from "./IERC20.sol";
import {IERC165} from "./IERC165.sol";
/**
* @title IERC1363
* @dev Interface of the ERC-1363 standard as defined in the https://eips.ethereum.org/EIPS/eip-1363[ERC-1363].
*
* Defines an extension interface for ERC-20 tokens that supports executing code on a recipient contract
* after `transfer` or `transferFrom`, or code on a spender contract after `approve`, in a single transaction.
*/
interface IERC1363 is IERC20, IERC165 {
/*
* Note: the ERC-165 identifier for this interface is 0xb0202a11.
* 0xb0202a11 ===
* bytes4(keccak256('transferAndCall(address,uint256)')) ^
* bytes4(keccak256('transferAndCall(address,uint256,bytes)')) ^
* bytes4(keccak256('transferFromAndCall(address,address,uint256)')) ^
* bytes4(keccak256('transferFromAndCall(address,address,uint256,bytes)')) ^
* bytes4(keccak256('approveAndCall(address,uint256)')) ^
* bytes4(keccak256('approveAndCall(address,uint256,bytes)'))
*/
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferAndCall(address to, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @param data Additional data with no specified format, sent in call to `to`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param from The address which you want to send tokens from.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferFromAndCall(address from, address to, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param from The address which you want to send tokens from.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @param data Additional data with no specified format, sent in call to `to`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferFromAndCall(address from, address to, uint256 value, bytes calldata data) external returns (bool);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
* @param spender The address which will spend the funds.
* @param value The amount of tokens to be spent.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function approveAndCall(address spender, uint256 value) external returns (bool);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
* @param spender The address which will spend the funds.
* @param value The amount of tokens to be spent.
* @param data Additional data with no specified format, sent in call to `spender`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function approveAndCall(address spender, uint256 value, bytes calldata data) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
pragma solidity ^0.8.20;
import {Context} from "../utils/Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* The initial owner is set to the address provided by the deployer. This can
* later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract Ownable is Context {
address private _owner;
/**
* @dev The caller account is not authorized to perform an operation.
*/
error OwnableUnauthorizedAccount(address account);
/**
* @dev The owner is not a valid owner account. (eg. `address(0)`)
*/
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the address provided by the deployer as the initial owner.
*/
constructor(address initialOwner) {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
/**
* @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 {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
/**
* @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 {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../token/ERC20/IERC20.sol";// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC165.sol)
pragma solidity ^0.8.20;
import {IERC165} from "../utils/introspection/IERC165.sol";// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/IERC165.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC-165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[ERC].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}{
"remappings": [
"@openzeppelin/=lib/openzeppelin-contracts/",
"ds-test/=lib/openzeppelin-contracts/lib/forge-std/lib/ds-test/src/",
"forge-std/=lib/forge-std/src/",
"openzeppelin-contracts/=lib/openzeppelin-contracts/",
"erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
"halmos-cheatcodes/=lib/openzeppelin-contracts/lib/halmos-cheatcodes/src/"
],
"optimizer": {
"enabled": true,
"runs": 1000
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "ipfs",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "shanghai",
"viaIR": false,
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"}],"name":"LiquidatorAddressChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferStarted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint256","name":"inputAmount","type":"uint256"},{"indexed":false,"internalType":"address","name":"inputToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"amountOut","type":"uint256"},{"indexed":false,"internalType":"address","name":"outputToken","type":"address"},{"indexed":false,"internalType":"int256","name":"slippage","type":"int256"},{"indexed":false,"internalType":"uint64","name":"referralCode","type":"uint64"},{"indexed":false,"internalType":"uint64","name":"referralFee","type":"uint64"},{"indexed":false,"internalType":"address","name":"referralFeeRecipient","type":"address"}],"name":"Swap","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"amountsIn","type":"uint256[]"},{"indexed":false,"internalType":"address[]","name":"tokensIn","type":"address[]"},{"indexed":false,"internalType":"uint256[]","name":"amountsOut","type":"uint256[]"},{"indexed":false,"internalType":"address[]","name":"tokensOut","type":"address[]"},{"indexed":false,"internalType":"int256[]","name":"slippage","type":"int256[]"},{"indexed":false,"internalType":"uint64","name":"referralCode","type":"uint64"},{"indexed":false,"internalType":"uint64","name":"referralFee","type":"uint64"},{"indexed":false,"internalType":"address","name":"referralFeeRecipient","type":"address"}],"name":"SwapMulti","type":"event"},{"inputs":[],"name":"FEE_DENOM","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"addressList","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"changeLiquidatorAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"liquidatorAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"inputToken","type":"address"},{"internalType":"uint256","name":"inputAmount","type":"uint256"},{"internalType":"address","name":"inputReceiver","type":"address"},{"internalType":"address","name":"outputToken","type":"address"},{"internalType":"uint256","name":"outputQuote","type":"uint256"},{"internalType":"uint256","name":"outputMin","type":"uint256"},{"internalType":"address","name":"outputReceiver","type":"address"}],"internalType":"struct IOdosRouterV3.swapTokenInfo","name":"tokenInfo","type":"tuple"},{"internalType":"bytes","name":"pathDefinition","type":"bytes"},{"internalType":"address","name":"executor","type":"address"},{"components":[{"internalType":"uint64","name":"code","type":"uint64"},{"internalType":"uint64","name":"fee","type":"uint64"},{"internalType":"address","name":"feeRecipient","type":"address"}],"internalType":"struct IOdosRouterV3.swapReferralInfo","name":"referralInfo","type":"tuple"}],"name":"swap","outputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"swapCompact","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"}],"internalType":"struct IOdosRouterV3.inputTokenInfo[]","name":"inputs","type":"tuple[]"},{"components":[{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint256","name":"amountQuote","type":"uint256"},{"internalType":"uint256","name":"amountMin","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"}],"internalType":"struct IOdosRouterV3.outputTokenInfo[]","name":"outputs","type":"tuple[]"},{"internalType":"bytes","name":"pathDefinition","type":"bytes"},{"internalType":"address","name":"executor","type":"address"},{"components":[{"internalType":"uint64","name":"code","type":"uint64"},{"internalType":"uint64","name":"fee","type":"uint64"},{"internalType":"address","name":"feeRecipient","type":"address"}],"internalType":"struct IOdosRouterV3.swapReferralInfo","name":"referralInfo","type":"tuple"}],"name":"swapMulti","outputs":[{"internalType":"uint256[]","name":"amountsOut","type":"uint256[]"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"swapMultiCompact","outputs":[{"internalType":"uint256[]","name":"amountsOut","type":"uint256[]"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"contractAddress","type":"address"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct IOdosRouterV3.permit2Info","name":"permit2","type":"tuple"},{"components":[{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"}],"internalType":"struct IOdosRouterV3.inputTokenInfo[]","name":"inputs","type":"tuple[]"},{"components":[{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint256","name":"amountQuote","type":"uint256"},{"internalType":"uint256","name":"amountMin","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"}],"internalType":"struct IOdosRouterV3.outputTokenInfo[]","name":"outputs","type":"tuple[]"},{"internalType":"bytes","name":"pathDefinition","type":"bytes"},{"internalType":"address","name":"executor","type":"address"},{"components":[{"internalType":"uint64","name":"code","type":"uint64"},{"internalType":"uint64","name":"fee","type":"uint64"},{"internalType":"address","name":"feeRecipient","type":"address"}],"internalType":"struct IOdosRouterV3.swapReferralInfo","name":"referralInfo","type":"tuple"}],"name":"swapMultiPermit2","outputs":[{"internalType":"uint256[]","name":"amountsOut","type":"uint256[]"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"contractAddress","type":"address"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct IOdosRouterV3.permit2Info","name":"permit2","type":"tuple"},{"components":[{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"}],"internalType":"struct IOdosRouterV3.inputTokenInfo[]","name":"inputs","type":"tuple[]"},{"components":[{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint256","name":"amountQuote","type":"uint256"},{"internalType":"uint256","name":"amountMin","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"}],"internalType":"struct IOdosRouterV3.outputTokenInfo[]","name":"outputs","type":"tuple[]"},{"internalType":"bytes","name":"pathDefinition","type":"bytes"},{"internalType":"address","name":"executor","type":"address"},{"components":[{"internalType":"uint64","name":"code","type":"uint64"},{"internalType":"uint64","name":"fee","type":"uint64"},{"internalType":"address","name":"feeRecipient","type":"address"}],"internalType":"struct IOdosRouterV3.swapReferralInfo","name":"referralInfo","type":"tuple"},{"internalType":"address","name":"hookTarget","type":"address"},{"internalType":"bytes","name":"hookData","type":"bytes"}],"name":"swapMultiPermit2WithHook","outputs":[{"internalType":"uint256[]","name":"amountsOut","type":"uint256[]"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"}],"internalType":"struct IOdosRouterV3.inputTokenInfo[]","name":"inputs","type":"tuple[]"},{"components":[{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint256","name":"amountQuote","type":"uint256"},{"internalType":"uint256","name":"amountMin","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"}],"internalType":"struct IOdosRouterV3.outputTokenInfo[]","name":"outputs","type":"tuple[]"},{"internalType":"bytes","name":"pathDefinition","type":"bytes"},{"internalType":"address","name":"executor","type":"address"},{"components":[{"internalType":"uint64","name":"code","type":"uint64"},{"internalType":"uint64","name":"fee","type":"uint64"},{"internalType":"address","name":"feeRecipient","type":"address"}],"internalType":"struct IOdosRouterV3.swapReferralInfo","name":"referralInfo","type":"tuple"},{"internalType":"address","name":"hookTarget","type":"address"},{"internalType":"bytes","name":"hookData","type":"bytes"}],"name":"swapMultiWithHook","outputs":[{"internalType":"uint256[]","name":"amountsOut","type":"uint256[]"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"contractAddress","type":"address"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct IOdosRouterV3.permit2Info","name":"permit2","type":"tuple"},{"components":[{"internalType":"address","name":"inputToken","type":"address"},{"internalType":"uint256","name":"inputAmount","type":"uint256"},{"internalType":"address","name":"inputReceiver","type":"address"},{"internalType":"address","name":"outputToken","type":"address"},{"internalType":"uint256","name":"outputQuote","type":"uint256"},{"internalType":"uint256","name":"outputMin","type":"uint256"},{"internalType":"address","name":"outputReceiver","type":"address"}],"internalType":"struct IOdosRouterV3.swapTokenInfo","name":"tokenInfo","type":"tuple"},{"internalType":"bytes","name":"pathDefinition","type":"bytes"},{"internalType":"address","name":"executor","type":"address"},{"components":[{"internalType":"uint64","name":"code","type":"uint64"},{"internalType":"uint64","name":"fee","type":"uint64"},{"internalType":"address","name":"feeRecipient","type":"address"}],"internalType":"struct IOdosRouterV3.swapReferralInfo","name":"referralInfo","type":"tuple"}],"name":"swapPermit2","outputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"contractAddress","type":"address"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct IOdosRouterV3.permit2Info","name":"permit2","type":"tuple"},{"components":[{"internalType":"address","name":"inputToken","type":"address"},{"internalType":"uint256","name":"inputAmount","type":"uint256"},{"internalType":"address","name":"inputReceiver","type":"address"},{"internalType":"address","name":"outputToken","type":"address"},{"internalType":"uint256","name":"outputQuote","type":"uint256"},{"internalType":"uint256","name":"outputMin","type":"uint256"},{"internalType":"address","name":"outputReceiver","type":"address"}],"internalType":"struct IOdosRouterV3.swapTokenInfo","name":"tokenInfo","type":"tuple"},{"internalType":"bytes","name":"pathDefinition","type":"bytes"},{"internalType":"address","name":"executor","type":"address"},{"components":[{"internalType":"uint64","name":"code","type":"uint64"},{"internalType":"uint64","name":"fee","type":"uint64"},{"internalType":"address","name":"feeRecipient","type":"address"}],"internalType":"struct IOdosRouterV3.swapReferralInfo","name":"referralInfo","type":"tuple"},{"internalType":"address","name":"hookTarget","type":"address"},{"internalType":"bytes","name":"hookData","type":"bytes"}],"name":"swapPermit2WithHook","outputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"}],"internalType":"struct IOdosRouterV3.inputTokenInfo[]","name":"inputs","type":"tuple[]"},{"components":[{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint256","name":"amountQuote","type":"uint256"},{"internalType":"uint256","name":"amountMin","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"}],"internalType":"struct IOdosRouterV3.outputTokenInfo[]","name":"outputs","type":"tuple[]"},{"internalType":"bytes","name":"pathDefinition","type":"bytes"},{"internalType":"address","name":"executor","type":"address"}],"name":"swapRouterFunds","outputs":[{"internalType":"uint256[]","name":"amountsOut","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"inputToken","type":"address"},{"internalType":"uint256","name":"inputAmount","type":"uint256"},{"internalType":"address","name":"inputReceiver","type":"address"},{"internalType":"address","name":"outputToken","type":"address"},{"internalType":"uint256","name":"outputQuote","type":"uint256"},{"internalType":"uint256","name":"outputMin","type":"uint256"},{"internalType":"address","name":"outputReceiver","type":"address"}],"internalType":"struct IOdosRouterV3.swapTokenInfo","name":"tokenInfo","type":"tuple"},{"internalType":"bytes","name":"pathDefinition","type":"bytes"},{"internalType":"address","name":"executor","type":"address"},{"components":[{"internalType":"uint64","name":"code","type":"uint64"},{"internalType":"uint64","name":"fee","type":"uint64"},{"internalType":"address","name":"feeRecipient","type":"address"}],"internalType":"struct IOdosRouterV3.swapReferralInfo","name":"referralInfo","type":"tuple"},{"internalType":"address","name":"hookTarget","type":"address"},{"internalType":"bytes","name":"hookData","type":"bytes"}],"name":"swapWithHook","outputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"tokens","type":"address[]"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"},{"internalType":"address","name":"dest","type":"address"}],"name":"transferRouterFunds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"addresses","type":"address[]"}],"name":"writeAddressList","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]Contract Creation Code
608060405234801562000010575f80fd5b5060405162004288380380620042888339810160408190526200003391620000e2565b806001600160a01b0381166200006257604051631e4fbdf760e01b81525f600482015260240160405180910390fd5b6200006d8162000075565b505062000111565b600180546001600160a01b0319169055620000908162000093565b50565b5f80546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b5f60208284031215620000f3575f80fd5b81516001600160a01b03811681146200010a575f80fd5b9392505050565b614169806200011f5f395ff3fe608060405260043610610170575f3560e01c806383bd37f9116100c6578063cab347311161007c578063e8668cac11610057578063e8668cac1461037d578063f2fde38b1461039c578063fef828dc146103bb575f80fd5b8063cab3473114610322578063e30c397814610341578063e5dae17d1461035e575f80fd5b80638da5cb5b116100ac5780638da5cb5b146102c857806398281469146102e4578063b810fb4314610303575f80fd5b806383bd37f9146102b857806384a7f3dd146102c0575f80fd5b80634886c67511610126578063715018a611610101578063715018a61461025957806375c111f21461026d57806379ba5097146102a4575f80fd5b80634886c6751461021857806357cfd3d414610233578063638cc0fa14610246575f80fd5b8063174da62111610156578063174da621146101b757806330f80b4c146101d85780633596f9a2146101f9575f80fd5b80630d459c081461017b578063108e3a77146101a4575f80fd5b3661017757005b5f80fd5b61018e610189366004613508565b6103ce565b60405161019b919061364f565b60405180910390f35b61018e6101b2366004613668565b61044f565b3480156101c2575f80fd5b506101d66101d1366004613777565b61046c565b005b6101eb6101e636600461388e565b6105ee565b60405190815260200161019b565b348015610204575f80fd5b506101d66102133660046138f8565b610606565b348015610223575f80fd5b506101eb670de0b6b3a764000081565b6101eb610241366004613937565b610689565b61018e6102543660046139ec565b610746565b348015610264575f80fd5b506101d66107c5565b348015610278575f80fd5b5060035461028c906001600160a01b031681565b6040516001600160a01b03909116815260200161019b565b3480156102af575f80fd5b506101d66107d8565b6101eb61081c565b61018e610a0f565b3480156102d3575f80fd5b505f546001600160a01b031661028c565b3480156102ef575f80fd5b5061018e6102fe366004613ad5565b610c7f565b34801561030e575f80fd5b5061028c61031d366004613b62565b6112c0565b34801561032d575f80fd5b506101eb61033c366004613b79565b6112e8565b34801561034c575f80fd5b506001546001600160a01b031661028c565b348015610369575f80fd5b506101d6610378366004613c14565b611302565b348015610388575f80fd5b506101eb610397366004613c2d565b611353565b3480156103a7575f80fd5b506101d66103b6366004613c14565b611412565b61018e6103c9366004613ce4565b611482565b60606103df8b8b8b8b8b8b8b611492565b604051633d318f9560e21b81529091506001600160a01b0385169063f4c63e5490610414908690869086903390600401613d81565b5f604051808303815f87803b15801561042b575f80fd5b505af115801561043d573d5f803e3d5ffd5b505050509a9950505050505050505050565b606061046088888888888888611492565b98975050505050505050565b6003546001600160a01b031633148061048e57505f546001600160a01b031633145b6104df5760405162461bcd60e51b815260206004820152601360248201527f41646472657373206e6f7420616c6c6f7765640000000000000000000000000060448201526064015b60405180910390fd5b83821461052e5760405162461bcd60e51b815260206004820152601660248201527f496e76616c69642066756e6473207472616e736665720000000000000000000060448201526064016104d6565b5f5b848110156105e6576105d486868381811061054d5761054d613dd8565b90506020020160208101906105629190613c14565b8386868581811061057557610575613dd8565b905060200201355f146105a05786868581811061059457610594613dd8565b905060200201356119e1565b6105cf8989868181106105b5576105b5613dd8565b90506020020160208101906105ca9190613c14565b61195a565b6119e1565b806105de81613e00565b915050610530565b505050505050565b5f6105fc8686868686611aa8565b9695505050505050565b61060e611c04565b5f5b8181101561068457600283838381811061062c5761062c613dd8565b90506020020160208101906106419190613c14565b81546001810183555f928352602090922090910180546001600160a01b0319166001600160a01b039092169190911790558061067c81613e00565b915050610610565b505050565b5f6106978989898989611aa8565b6040805160018082528183019092529192505f91906020808301908036833701905050905081815f815181106106cf576106cf613dd8565b6020908102919091010152604051633d318f9560e21b81526001600160a01b0386169063f4c63e549061070c908790879086903390600401613d81565b5f604051808303815f87803b158015610723575f80fd5b505af1158015610735573d5f803e3d5ffd5b505050505098975050505050505050565b60606107568a8a8a8a8a8a611c30565b604051633d318f9560e21b81529091506001600160a01b0385169063f4c63e549061078b908690869086903390600401613d81565b5f604051808303815f87803b1580156107a2575f80fd5b505af11580156107b4573d5f803e3d5ffd5b505050509998505050505050505050565b6107cd611c04565b6107d65f611e84565b565b60015433906001600160a01b031681146108105760405163118cdaa760e01b81526001600160a01b03821660048201526024016104d6565b61081981611e84565b50565b6040805160e0810182525f80825260208201819052918101829052606081018290526080810182905260a0810182905260c08101829052604080516060810182525f80825260208201819052918101919091525f365f6108e7565b5f80823560f01c8080156108be57600181146108c9577f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5acc82015493506002850192506108e0565b6002850192506108e0565b6001600160a01b03853560501c1693506016850192505b5050915091565b5f60046108f381610877565b9150915081875261090381610877565b606089018290529092506001810191503560f81c8015610932578135600882602003021c602089015280820191505b50803560f81c6001820191508135600882602003021c8060808a01528183019250823560e81c915062ffffff8262ffffff0382020460a08a0152505060038101905061097d81610877565b9150945061098a81610877565b909250905081610998578491505b8160408801526109a781610877565b60c089810192909252803590911c8752600881013560f81c9250600901905081156109e757803560c01c6020870152600881013560601c6040870152601c015b6001810193503560f81c6020029150610a0590508583838688611aa8565b9550505050505090565b60605f8180600660043560f890811c90600535901c8167ffffffffffffffff811115610a3d57610a3d61318c565b604051908082528060200260200182016040528015610a8657816020015b604080516060810182525f80825260208083018290529282015282525f19909201910181610a5b5790505b5094508067ffffffffffffffff811115610aa257610aa261318c565b604051908082528060200260200182016040528015610af257816020015b604080516080810182525f8082526020808301829052928201819052606082015282525f19909201910181610ac05790505b509350610afe83610877565b9096506003810193503560e81c5f80805b85811015610b7c576020808202018901519150610b2b87610877565b8184526001810198509093503560f81c8015610b56578735600882602003021c602084015280880197505b50610b6087610877565b9750925082610b6d578992505b60408201839052600101610b0f565b505f5b84811015610bf2576020808202018801519150610b9b87610877565b97509250828252863560f81c6001880197508735600882602003021c806020850152818901985062ffffff8662ffffff0382020460408501525050610bdf87610877565b6060840182905297509250600101610b7f565b5050604080516060810182525f80825260208201819052918101919091529350610c1b92505050565b813560c01c8152600982019136905f906008013560f81c8015610c5657843560c01c6020850152600885013560601c6040850152601c909401935b505050600182016020833560f81c02610c73868684848b88611c30565b97505050505050505090565b6003546060906001600160a01b0316331480610ca457505f546001600160a01b031633145b610cf05760405162461bcd60e51b815260206004820152601360248201527f41646472657373206e6f7420616c6c6f7765640000000000000000000000000060448201526064016104d6565b5f865167ffffffffffffffff811115610d0b57610d0b61318c565b604051908082528060200260200182016040528015610d34578160200160208202803683370190505b5090505f875167ffffffffffffffff811115610d5257610d5261318c565b604051908082528060200260200182016040528015610d7b578160200160208202803683370190505b5090505f5b8851811015610ecb57888181518110610d9b57610d9b613dd8565b60200260200101515f0151828281518110610db857610db8613dd8565b60200260200101906001600160a01b031690816001600160a01b031681525050888181518110610dea57610dea613dd8565b6020026020010151602001515f14610e1f57888181518110610e0e57610e0e613dd8565b602002602001015160200151610e41565b610e41828281518110610e3457610e34613dd8565b602002602001015161195a565b838281518110610e5357610e53613dd8565b602002602001018181525050610eb9828281518110610e7457610e74613dd8565b60200260200101518a8381518110610e8e57610e8e613dd8565b602002602001015160400151858481518110610eac57610eac613dd8565b60200260200101516119e1565b80610ec381613e00565b915050610d80565b505f875167ffffffffffffffff811115610ee757610ee761318c565b604051908082528060200260200182016040528015610f10578160200160208202803683370190505b5090505f885167ffffffffffffffff811115610f2e57610f2e61318c565b604051908082528060200260200182016040528015610f57578160200160208202803683370190505b5090505f5b8951811015610ff857898181518110610f7757610f77613dd8565b60200260200101515f0151828281518110610f9457610f94613dd8565b60200260200101906001600160a01b031690816001600160a01b031681525050610fc9828281518110610e3457610e34613dd8565b838281518110610fdb57610fdb613dd8565b602090810291909101015280610ff081613e00565b915050610f5c565b5060405163cb70e27360e01b81526001600160a01b0387169063cb70e273905f9061102d908c908c908a903390600401613d81565b5f604051808303818588803b158015611044575f80fd5b505af1158015611056573d5f803e3d5ffd5b5050505050885167ffffffffffffffff8111156110755761107561318c565b60405190808252806020026020018201604052801561109e578160200160208202803683370190505b5094505f5b8951811015611228578281815181106110be576110be613dd8565b60200260200101516110db838381518110610e3457610e34613dd8565b6110e59190613e18565b8682815181106110f7576110f7613dd8565b60200260200101818152505089818151811061111557611115613dd8565b60200260200101516040015186828151811061113357611133613dd8565b602002602001015110156111895760405162461bcd60e51b815260206004820152601760248201527f536c697070616765204c696d697420457863656564656400000000000000000060448201526064016104d6565b6112168a828151811061119e5761119e613dd8565b60200260200101515f01515f6001600160a01b03168c84815181106111c5576111c5613dd8565b6020026020010151606001516001600160a01b031614611202578b83815181106111f1576111f1613dd8565b602002602001015160600151611204565b335b888481518110610eac57610eac613dd8565b8061122081613e00565b9150506110a3565b507f2c96555a96d94780f3a97aeb724514e80e331842f3143742d85da5aa68df9d3033858588858e5167ffffffffffffffff8111156112695761126961318c565b604051908082528060200260200182016040528015611292578160200160208202803683370190505b505f805f6040516112ab99989796959493929190613e62565b60405180910390a15050505095945050505050565b600281815481106112cf575f80fd5b5f918252602090912001546001600160a01b0316905081565b5f6112f7878787878787611e9d565b979650505050505050565b61130a611c04565b600380546001600160a01b0319166001600160a01b0383169081179091556040517f1535fa8f7275b71050af30bf7f74391b45be7ba2b545fd28279dafe9b50f6424905f90a250565b5f6113628a8a8a8a8a8a611e9d565b6040805160018082528183019092529192505f91906020808301908036833701905050905081815f8151811061139a5761139a613dd8565b6020908102919091010152604051633d318f9560e21b81526001600160a01b0386169063f4c63e54906113d7908790879086903390600401613d81565b5f604051808303815f87803b1580156113ee575f80fd5b505af1158015611400573d5f803e3d5ffd5b50505050509998505050505050505050565b61141a611c04565b600180546001600160a01b0383166001600160a01b0319909116811790915561144a5f546001600160a01b031690565b6001600160a01b03167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270060405160405180910390a350565b60606112f7878787878787611c30565b60606114b66040518060600160405280606081526020015f81526020015f81525090565b60605f8034116114c75789516114d5565b60018a516114d59190613e18565b905060405180606001604052808267ffffffffffffffff8111156114fb576114fb61318c565b60405190808252806020026020018201604052801561153f57816020015b604080518082019091525f80825260208201528152602001906001900390816115195790505b5081526020018c6020015181526020018c6040015181525092508067ffffffffffffffff8111156115725761157261318c565b6040519080825280602002602001820160405280156115b657816020015b604080518082019091525f80825260208201528152602001906001900390816115905790505b509150505f805b8a5181101561187c575f6001600160a01b03168b82815181106115e2576115e2613dd8565b60200260200101515f01516001600160a01b031603611668578a818151811061160d5761160d613dd8565b6020026020010151602001515f0361164357348b828151811061163257611632613dd8565b602002602001015160200181815250505b8a818151811061165557611655613dd8565b602002602001015160200151915061186a565b8a818151811061167a5761167a613dd8565b6020026020010151602001515f03611734578a818151811061169e5761169e613dd8565b6020908102919091010151516040516370a0823160e01b81523360048201526001600160a01b03909116906370a0823190602401602060405180830381865afa1580156116ed573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906117119190613f07565b8b828151811061172357611723613dd8565b602002602001015160200181815250505b5f821561174b57611746600183613e18565b61174d565b815b90508b828151811061176157611761613dd8565b60200260200101515f0151855f0151828151811061178157611781613dd8565b60209081029190910101516001600160a01b0390911690528b518c90839081106117ad576117ad613dd8565b602002602001015160200151855f015182815181106117ce576117ce613dd8565b602002602001015160200181815250508b82815181106117f0576117f0613dd8565b60200260200101516040015184828151811061180e5761180e613dd8565b60209081029190910101516001600160a01b0390911690528b518c908390811061183a5761183a613dd8565b60200260200101516020015184828151811061185857611858613dd8565b60200260200101516020018181525050505b8061187481613e00565b9150506115bd565b508034146118be5760405162461bcd60e51b815260206004820152600f60248201526e57726f6e67206d73672e76616c756560881b60448201526064016104d6565b50895160608b01516040517fedd9444b0000000000000000000000000000000000000000000000000000000081526001600160a01b039092169163edd9444b916119119186918691339190600401613fac565b5f604051808303815f87803b158015611928575f80fd5b505af115801561193a573d5f803e3d5ffd5b5050505061194c898989898989611f72565b9a9950505050505050505050565b5f6001600160a01b038216611970575047919050565b6040516370a0823160e01b81523060048201526001600160a01b038316906370a0823190602401602060405180830381865afa1580156119b2573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906119d69190613f07565b92915050565b919050565b6001600160a01b038316611a94575f826001600160a01b0316826040515f6040518083038185875af1925050503d805f8114611a38576040519150601f19603f3d011682016040523d82523d5f602084013e611a3d565b606091505b5050905080611a8e5760405162461bcd60e51b815260206004820152601360248201527f455448207472616e73666572206661696c65640000000000000000000000000060448201526064016104d6565b50505050565b6106846001600160a01b0384168383612b02565b84515f906001600160a01b0316611b195785602001515f03611acf57346020870152611bf7565b85602001513414611b145760405162461bcd60e51b815260206004820152600f60248201526e57726f6e67206d73672e76616c756560881b60448201526064016104d6565b611bf7565b3415611b595760405162461bcd60e51b815260206004820152600f60248201526e57726f6e67206d73672e76616c756560881b60448201526064016104d6565b85602001515f03611bd35785516040516370a0823160e01b81523360048201526001600160a01b03909116906370a0823190602401602060405180830381865afa158015611ba9573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611bcd9190613f07565b60208701525b604086015160208701518751611bf7926001600160a01b0390911691339190612b76565b6105fc8686868686612baf565b5f546001600160a01b031633146107d65760405163118cdaa760e01b81523360048201526024016104d6565b60605f805b8851811015611e34575f6001600160a01b0316898281518110611c5a57611c5a613dd8565b60200260200101515f01516001600160a01b031603611ce057888181518110611c8557611c85613dd8565b6020026020010151602001515f03611cbb5734898281518110611caa57611caa613dd8565b602002602001015160200181815250505b888181518110611ccd57611ccd613dd8565b6020026020010151602001519150611e22565b888181518110611cf257611cf2613dd8565b6020026020010151602001515f03611dac57888181518110611d1657611d16613dd8565b6020908102919091010151516040516370a0823160e01b81523360048201526001600160a01b03909116906370a0823190602401602060405180830381865afa158015611d65573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611d899190613f07565b898281518110611d9b57611d9b613dd8565b602002602001015160200181815250505b611e22338a8381518110611dc257611dc2613dd8565b6020026020010151604001518b8481518110611de057611de0613dd8565b6020026020010151602001518c8581518110611dfe57611dfe613dd8565b60200260200101515f01516001600160a01b0316612b76909392919063ffffffff16565b80611e2c81613e00565b915050611c35565b50803414611e765760405162461bcd60e51b815260206004820152600f60248201526e57726f6e67206d73672e76616c756560881b60448201526064016104d6565b610460888888888888611f72565b600180546001600160a01b0319169055610819816130b8565b85516040805160a08101825287516001600160a01b0390811660608084019182526020808c0180516080870152928552808d015181860152858d015185870152855180870187528c870151851681529251908301528b015193517f30f28b7a0000000000000000000000000000000000000000000000000000000081525f9592909216936330f28b7a93611f38939092913391600401614060565b5f604051808303815f87803b158015611f4f575f80fd5b505af1158015611f61573d5f803e3d5ffd5b505050506112f78686868686612baf565b60605f875167ffffffffffffffff811115611f8f57611f8f61318c565b604051908082528060200260200182016040528015611fb8578160200160208202803683370190505b5090505f885167ffffffffffffffff811115611fd657611fd661318c565b604051908082528060200260200182016040528015611fff578160200160208202803683370190505b5090505f5b89518110156122175789818151811061201f5761201f613dd8565b60200260200101516020015183828151811061203d5761203d613dd8565b60200260200101818152505089818151811061205b5761205b613dd8565b60200260200101515f015182828151811061207857612078613dd8565b60200260200101906001600160a01b031690816001600160a01b0316815250505f5b8181101561214d578a81815181106120b4576120b4613dd8565b60200260200101515f01516001600160a01b03168b83815181106120da576120da613dd8565b60200260200101515f01516001600160a01b03160361213b5760405162461bcd60e51b815260206004820152601760248201527f4475706c696361746520736f7572636520746f6b656e7300000000000000000060448201526064016104d6565b8061214581613e00565b91505061209a565b505f5b89518110156122045789818151811061216b5761216b613dd8565b60200260200101515f01516001600160a01b03168b838151811061219157612191613dd8565b60200260200101515f01516001600160a01b0316036121f25760405162461bcd60e51b815260206004820152601760248201527f417262697472616765206e6f7420737570706f7274656400000000000000000060448201526064016104d6565b806121fc81613e00565b915050612150565b508061220f81613e00565b915050612004565b505f885167ffffffffffffffff8111156122335761223361318c565b60405190808252806020026020018201604052801561225c578160200160208202803683370190505b5090505f5b895181101561246a5789818151811061227c5761227c613dd8565b6020026020010151602001518a828151811061229a5761229a613dd8565b60200260200101516040015111156122f45760405162461bcd60e51b815260206004820152601a60248201527f4d696e696d756d2067726561746572207468616e2071756f746500000000000060448201526064016104d6565b5f8a828151811061230757612307613dd8565b602002602001015160400151116123605760405162461bcd60e51b815260206004820152601660248201527f4d696e696d756d206f7574707574206973207a65726f0000000000000000000060448201526064016104d6565b5f5b81811015612415578a818151811061237c5761237c613dd8565b60200260200101515f01516001600160a01b03168b83815181106123a2576123a2613dd8565b60200260200101515f01516001600160a01b0316036124035760405162461bcd60e51b815260206004820152601c60248201527f4475706c69636174652064657374696e6174696f6e20746f6b656e730000000060448201526064016104d6565b8061240d81613e00565b915050612362565b5061243b8a828151811061242b5761242b613dd8565b60200260200101515f015161195a565b82828151811061244d5761244d613dd8565b60209081029190910101528061246281613e00565b915050612261565b5060405163cb70e27360e01b81526001600160a01b0387169063cb70e27390349061249f908c908c9089903390600401613d81565b5f604051808303818588803b1580156124b6575f80fd5b505af11580156124c8573d5f803e3d5ffd5b50505050505f895167ffffffffffffffff8111156124e8576124e861318c565b604051908082528060200260200182016040528015612511578160200160208202803683370190505b509050895167ffffffffffffffff81111561252e5761252e61318c565b604051908082528060200260200182016040528015612557578160200160208202803683370190505b50865190955060201c61ffff165f8190036125715750611f405b6127108111156125c35760405162461bcd60e51b815260206004820152601060248201527f496e76616c69642052656620436f64650000000000000000000000000000000060448201526064016104d6565b5f5b8b518110156129f7578381815181106125e0576125e0613dd8565b60200260200101516125fd8d838151811061242b5761242b613dd8565b6126079190613e18565b87828151811061261957612619613dd8565b6020026020010181815250505f886020015167ffffffffffffffff1611156128035760408801516001600160a01b03166126955760405162461bcd60e51b815260206004820152601260248201527f4e756c6c2066656520726563697069656e74000000000000000000000000000060448201526064016104d6565b6126a86032670de0b6b3a76400006140d7565b886020015167ffffffffffffffff1611156126f45760405162461bcd60e51b815260206004820152600c60248201526b08ccaca40e8dede40d0d2ced60a31b60448201526064016104d6565b60408801516001600160a01b0316301461278b5761278b8c828151811061271d5761271d613dd8565b60200260200101515f01518960400151670de0b6b3a764000061271061274391906140f6565b858c6020015167ffffffffffffffff168c878151811061276557612765613dd8565b602002602001015161277791906140f6565b61278191906140f6565b6105cf91906140d7565b670de0b6b3a7640000886020015167ffffffffffffffff16670de0b6b3a76400006127b69190613e18565b8883815181106127c8576127c8613dd8565b60200260200101516127da91906140f6565b6127e491906140d7565b8782815181106127f6576127f6613dd8565b6020026020010181815250505b8b818151811061281557612815613dd8565b60200260200101516020015187828151811061283357612833613dd8565b6020026020010151612845919061410d565b83828151811061285757612857613dd8565b6020026020010181815250505f83828151811061287657612876613dd8565b60200260200101511380156128905750875160301c600116155b156128d2578b81815181106128a7576128a7613dd8565b6020026020010151602001518782815181106128c5576128c5613dd8565b6020026020010181815250505b8b81815181106128e4576128e4613dd8565b60200260200101516040015187828151811061290257612902613dd8565b602002602001015110156129585760405162461bcd60e51b815260206004820152601760248201527f536c697070616765204c696d697420457863656564656400000000000000000060448201526064016104d6565b6129e58c828151811061296d5761296d613dd8565b60200260200101515f01515f6001600160a01b03168e848151811061299457612994613dd8565b6020026020010151606001516001600160a01b0316146129d1578d83815181106129c0576129c0613dd8565b6020026020010151606001516129d3565b335b898481518110610eac57610eac613dd8565b806129ef81613e00565b9150506125c5565b50505f8a5167ffffffffffffffff811115612a1457612a1461318c565b604051908082528060200260200182016040528015612a3d578160200160208202803683370190505b5090505f5b8b51811015612aa4578b8181518110612a5d57612a5d613dd8565b60200260200101515f0151828281518110612a7a57612a7a613dd8565b6001600160a01b039092166020928302919091019091015280612a9c81613e00565b915050612a42565b50865160208801516040808a015190517f2c96555a96d94780f3a97aeb724514e80e331842f3143742d85da5aa68df9d3093612aeb9333938b938b938e938a938c93613e62565b60405180910390a150505050509695505050505050565b6040516001600160a01b0383811660248301526044820183905261068491859182169063a9059cbb906064015b604051602081830303815290604052915060e01b6020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050613107565b6040516001600160a01b038481166024830152838116604483015260648201839052611a8e9186918216906323b872dd90608401612b2f565b5f85608001518660a001511115612c085760405162461bcd60e51b815260206004820152601a60248201527f4d696e696d756d2067726561746572207468616e2071756f746500000000000060448201526064016104d6565b5f8660a0015111612c5b5760405162461bcd60e51b815260206004820152601660248201527f4d696e696d756d206f7574707574206973207a65726f0000000000000000000060448201526064016104d6565b85606001516001600160a01b0316865f01516001600160a01b031603612cc35760405162461bcd60e51b815260206004820152601760248201527f417262697472616765206e6f7420737570706f7274656400000000000000000060448201526064016104d6565b5f612cd1876060015161195a565b6040805160018082528183019092529192505f9190602080830190803683370190505090508760200151815f81518110612d0d57612d0d613dd8565b602090810291909101015260405163cb70e27360e01b81526001600160a01b0386169063cb70e273903490612d4c908b908b9087903390600401613d81565b5f604051808303818588803b158015612d63575f80fd5b505af1158015612d75573d5f803e3d5ffd5b505050505081612d88896060015161195a565b612d929190613e18565b602085015190935067ffffffffffffffff1615612f5f5760408401516001600160a01b0316612e035760405162461bcd60e51b815260206004820152601260248201527f4e756c6c2066656520726563697069656e74000000000000000000000000000060448201526064016104d6565b612e166032670de0b6b3a76400006140d7565b846020015167ffffffffffffffff161115612e625760405162461bcd60e51b815260206004820152600c60248201526b08ccaca40e8dede40d0d2ced60a31b60448201526064016104d6565b835160201c61ffff165f819003612e785750611f405b612710811115612eca5760405162461bcd60e51b815260206004820152601060248201527f496e76616c69642052656620436f64650000000000000000000000000000000060448201526064016104d6565b60408501516001600160a01b03163014612f1c57612f1c89606001518660400151670de0b6b3a7640000612710612f0191906140f6565b84896020015167ffffffffffffffff168961277791906140f6565b670de0b6b3a7640000856020015167ffffffffffffffff16670de0b6b3a7640000612f479190613e18565b612f5190866140f6565b612f5b91906140d7565b9350505b5f886080015184612f70919061410d565b90505f81138015612f865750845160301c600116155b15612f9357886080015193505b8860a00151841015612fe75760405162461bcd60e51b815260206004820152601760248201527f536c697070616765204c696d697420457863656564656400000000000000000060448201526064016104d6565b606089015160c08a015161301691906001600160a01b03161561300e578a60c00151613010565b335b866119e1565b6020808a01518a516060808d015189518a8601516040808d01518151338152988901979097526001600160a01b03958616908801529286018a9052908316608086015260a0850186905267ffffffffffffffff90811660c08601521660e0840152166101008201527f69db20ca9e32403e6c56e5193b3e3b2827ae5c430ccfdea392ba950d2d1ab2bc906101200160405180910390a150505095945050505050565b5f80546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b5f8060205f8451602086015f885af180613126576040513d5f823e3d81fd5b50505f513d9150811561313d57806001141561314a565b6001600160a01b0384163b155b15611a8e576040517f5274afe70000000000000000000000000000000000000000000000000000000081526001600160a01b03851660048201526024016104d6565b634e487b7160e01b5f52604160045260245ffd5b6040516080810167ffffffffffffffff811182821017156131c3576131c361318c565b60405290565b6040516060810167ffffffffffffffff811182821017156131c3576131c361318c565b604051601f8201601f1916810167ffffffffffffffff811182821017156132155761321561318c565b604052919050565b80356001600160a01b03811681146119dc575f80fd5b5f60808284031215613243575f80fd5b61324b6131a0565b90506132568261321d565b81526020808301358183015260408301356040830152606083013567ffffffffffffffff80821115613286575f80fd5b818501915085601f830112613299575f80fd5b8135818111156132ab576132ab61318c565b6132bd601f8201601f191685016131ec565b915080825286848285010111156132d2575f80fd5b80848401858401375f8482840101525080606085015250505092915050565b5f67ffffffffffffffff82111561330a5761330a61318c565b5060051b60200190565b5f82601f830112613323575f80fd5b81356020613338613333836132f1565b6131ec565b82815260609283028501820192828201919087851115613356575f80fd5b8387015b858110156133aa5781818a031215613371575f8081fd5b6133796131c9565b6133828261321d565b81528582013586820152604061339981840161321d565b90820152845292840192810161335a565b5090979650505050505050565b5f82601f8301126133c6575f80fd5b813560206133d6613333836132f1565b82815260079290921b840181019181810190868411156133f4575f80fd5b8286015b848110156134545760808189031215613410575f8081fd5b6134186131a0565b6134218261321d565b8152818501358582015260408083013590820152606061344281840161321d565b908201528352918301916080016133f8565b509695505050505050565b5f8083601f84011261346f575f80fd5b50813567ffffffffffffffff811115613486575f80fd5b60208301915083602082850101111561349d575f80fd5b9250929050565b803567ffffffffffffffff811681146119dc575f80fd5b5f606082840312156134cb575f80fd5b6134d36131c9565b90506134de826134a4565b81526134ec602083016134a4565b60208201526134fd6040830161321d565b604082015292915050565b5f805f805f805f805f806101408b8d031215613522575f80fd5b8a3567ffffffffffffffff80821115613539575f80fd5b6135458e838f01613233565b9b5060208d013591508082111561355a575f80fd5b6135668e838f01613314565b9a5060408d013591508082111561357b575f80fd5b6135878e838f016133b7565b995060608d013591508082111561359c575f80fd5b6135a88e838f0161345f565b90995097508791506135bc60808e0161321d565b96506135cb8e60a08f016134bb565b95506135da6101008e0161321d565b94506101208d01359150808211156135f0575f80fd5b506135fd8d828e0161345f565b915080935050809150509295989b9194979a5092959850565b5f8151808452602080850194508084015f5b8381101561364457815187529582019590820190600101613628565b509495945050505050565b602081525f6136616020830184613616565b9392505050565b5f805f805f805f610100888a03121561367f575f80fd5b873567ffffffffffffffff80821115613696575f80fd5b6136a28b838c01613233565b985060208a01359150808211156136b7575f80fd5b6136c38b838c01613314565b975060408a01359150808211156136d8575f80fd5b6136e48b838c016133b7565b965060608a01359150808211156136f9575f80fd5b506137068a828b0161345f565b909550935061371990506080890161321d565b91506137288960a08a016134bb565b905092959891949750929550565b5f8083601f840112613746575f80fd5b50813567ffffffffffffffff81111561375d575f80fd5b6020830191508360208260051b850101111561349d575f80fd5b5f805f805f6060868803121561378b575f80fd5b853567ffffffffffffffff808211156137a2575f80fd5b6137ae89838a01613736565b909750955060208801359150808211156137c6575f80fd5b506137d388828901613736565b90945092506137e690506040870161321d565b90509295509295909350565b5f60e08284031215613802575f80fd5b60405160e0810181811067ffffffffffffffff821117156138255761382561318c565b6040529050806138348361321d565b81526020830135602082015261384c6040840161321d565b604082015261385d6060840161321d565b60608201526080830135608082015260a083013560a082015261388260c0840161321d565b60c08201525092915050565b5f805f805f61018086880312156138a3575f80fd5b6138ad87876137f2565b945060e086013567ffffffffffffffff8111156138c8575f80fd5b6138d48882890161345f565b90955093506138e89050610100870161321d565b91506137e68761012088016134bb565b5f8060208385031215613909575f80fd5b823567ffffffffffffffff81111561391f575f80fd5b61392b85828601613736565b90969095509350505050565b5f805f805f805f806101c0898b03121561394f575f80fd5b6139598a8a6137f2565b975060e089013567ffffffffffffffff80821115613975575f80fd5b6139818c838d0161345f565b90995097508791506139966101008c0161321d565b96506139a68c6101208d016134bb565b95506139b56101808c0161321d565b94506101a08b01359150808211156139cb575f80fd5b506139d88b828c0161345f565b999c989b5096995094979396929594505050565b5f805f805f805f805f6101208a8c031215613a05575f80fd5b893567ffffffffffffffff80821115613a1c575f80fd5b613a288d838e01613314565b9a5060208c0135915080821115613a3d575f80fd5b613a498d838e016133b7565b995060408c0135915080821115613a5e575f80fd5b613a6a8d838e0161345f565b9099509750879150613a7e60608d0161321d565b9650613a8d8d60808e016134bb565b9550613a9b60e08d0161321d565b94506101008c0135915080821115613ab1575f80fd5b50613abe8c828d0161345f565b915080935050809150509295985092959850929598565b5f805f805f60808688031215613ae9575f80fd5b853567ffffffffffffffff80821115613b00575f80fd5b613b0c89838a01613314565b96506020880135915080821115613b21575f80fd5b613b2d89838a016133b7565b95506040880135915080821115613b42575f80fd5b50613b4f8882890161345f565b90945092506137e690506060870161321d565b5f60208284031215613b72575f80fd5b5035919050565b5f805f805f806101a08789031215613b8f575f80fd5b863567ffffffffffffffff80821115613ba6575f80fd5b613bb28a838b01613233565b9750613bc18a60208b016137f2565b9650610100890135915080821115613bd7575f80fd5b50613be489828a0161345f565b9095509350613bf89050610120880161321d565b9150613c088861014089016134bb565b90509295509295509295565b5f60208284031215613c24575f80fd5b6136618261321d565b5f805f805f805f805f6101e08a8c031215613c46575f80fd5b893567ffffffffffffffff80821115613c5d575f80fd5b613c698d838e01613233565b9a50613c788d60208e016137f2565b99506101008c0135915080821115613c8e575f80fd5b613c9a8d838e0161345f565b9099509750879150613caf6101208d0161321d565b9650613cbf8d6101408e016134bb565b9550613cce6101a08d0161321d565b94506101c08c0135915080821115613ab1575f80fd5b5f805f805f8060e08789031215613cf9575f80fd5b863567ffffffffffffffff80821115613d10575f80fd5b613d1c8a838b01613314565b97506020890135915080821115613d31575f80fd5b613d3d8a838b016133b7565b96506040890135915080821115613d52575f80fd5b50613d5f89828a0161345f565b9095509350613d7290506060880161321d565b9150613c0888608089016134bb565b60608152836060820152838560808301375f608085830101525f601f19601f86011682016080838203016020840152613dbd6080820186613616565b9150506001600160a01b038316604083015295945050505050565b634e487b7160e01b5f52603260045260245ffd5b634e487b7160e01b5f52601160045260245ffd5b5f60018201613e1157613e11613dec565b5060010190565b818103818111156119d6576119d6613dec565b5f8151808452602080850194508084015f5b838110156136445781516001600160a01b031687529582019590820190600101613e3d565b5f6101206001600160a01b03808d168452816020850152613e858285018d613616565b91508382036040850152613e99828c613e2b565b91508382036060850152613ead828b613616565b91508382036080850152613ec1828a613e2b565b915083820360a0850152613ed58289613616565b67ffffffffffffffff97881660c08601529590961660e084015250509216610100909201919091529695505050505050565b5f60208284031215613f17575f80fd5b5051919050565b5f8151808452602080850194508084015f5b8381101561364457613f5687835180516001600160a01b03168252602090810151910152565b6040969096019590820190600101613f30565b5f81518084525f5b81811015613f8d57602081850181015186830182015201613f71565b505f602082860101526020601f19601f83011685010191505092915050565b608081525f60e08201865160606080850152818151808452610100860191506020935083830192505f5b8181101561400f57613ffc83855180516001600160a01b03168252602090810151910152565b9284019260409290920191600101613fd6565b50508289015160a0860152604089015160c0860152848103838601526140358189613f1e565b9250505061404e60408401866001600160a01b03169052565b82810360608401526112f78185613f69565b5f61010061408283885180516001600160a01b03168252602090810151910152565b60208701516040840152604087015160608401526140b6608084018780516001600160a01b03168252602090810151910152565b6001600160a01b03851660c08401528060e08401526112f781840185613f69565b5f826140f157634e487b7160e01b5f52601260045260245ffd5b500490565b80820281158282048414176119d6576119d6613dec565b8181035f83128015838313168383128216171561412c5761412c613dec565b509291505056fea26469706673582212201f12ee48d2d74a83b6ae0555a0c64d60e509b162a6d823cd98185fee561d0a3364736f6c63430008140033000000000000000000000000000636843c30b6b10d3dc9af803e7a7956aa994c
Deployed Bytecode
0x608060405260043610610170575f3560e01c806383bd37f9116100c6578063cab347311161007c578063e8668cac11610057578063e8668cac1461037d578063f2fde38b1461039c578063fef828dc146103bb575f80fd5b8063cab3473114610322578063e30c397814610341578063e5dae17d1461035e575f80fd5b80638da5cb5b116100ac5780638da5cb5b146102c857806398281469146102e4578063b810fb4314610303575f80fd5b806383bd37f9146102b857806384a7f3dd146102c0575f80fd5b80634886c67511610126578063715018a611610101578063715018a61461025957806375c111f21461026d57806379ba5097146102a4575f80fd5b80634886c6751461021857806357cfd3d414610233578063638cc0fa14610246575f80fd5b8063174da62111610156578063174da621146101b757806330f80b4c146101d85780633596f9a2146101f9575f80fd5b80630d459c081461017b578063108e3a77146101a4575f80fd5b3661017757005b5f80fd5b61018e610189366004613508565b6103ce565b60405161019b919061364f565b60405180910390f35b61018e6101b2366004613668565b61044f565b3480156101c2575f80fd5b506101d66101d1366004613777565b61046c565b005b6101eb6101e636600461388e565b6105ee565b60405190815260200161019b565b348015610204575f80fd5b506101d66102133660046138f8565b610606565b348015610223575f80fd5b506101eb670de0b6b3a764000081565b6101eb610241366004613937565b610689565b61018e6102543660046139ec565b610746565b348015610264575f80fd5b506101d66107c5565b348015610278575f80fd5b5060035461028c906001600160a01b031681565b6040516001600160a01b03909116815260200161019b565b3480156102af575f80fd5b506101d66107d8565b6101eb61081c565b61018e610a0f565b3480156102d3575f80fd5b505f546001600160a01b031661028c565b3480156102ef575f80fd5b5061018e6102fe366004613ad5565b610c7f565b34801561030e575f80fd5b5061028c61031d366004613b62565b6112c0565b34801561032d575f80fd5b506101eb61033c366004613b79565b6112e8565b34801561034c575f80fd5b506001546001600160a01b031661028c565b348015610369575f80fd5b506101d6610378366004613c14565b611302565b348015610388575f80fd5b506101eb610397366004613c2d565b611353565b3480156103a7575f80fd5b506101d66103b6366004613c14565b611412565b61018e6103c9366004613ce4565b611482565b60606103df8b8b8b8b8b8b8b611492565b604051633d318f9560e21b81529091506001600160a01b0385169063f4c63e5490610414908690869086903390600401613d81565b5f604051808303815f87803b15801561042b575f80fd5b505af115801561043d573d5f803e3d5ffd5b505050509a9950505050505050505050565b606061046088888888888888611492565b98975050505050505050565b6003546001600160a01b031633148061048e57505f546001600160a01b031633145b6104df5760405162461bcd60e51b815260206004820152601360248201527f41646472657373206e6f7420616c6c6f7765640000000000000000000000000060448201526064015b60405180910390fd5b83821461052e5760405162461bcd60e51b815260206004820152601660248201527f496e76616c69642066756e6473207472616e736665720000000000000000000060448201526064016104d6565b5f5b848110156105e6576105d486868381811061054d5761054d613dd8565b90506020020160208101906105629190613c14565b8386868581811061057557610575613dd8565b905060200201355f146105a05786868581811061059457610594613dd8565b905060200201356119e1565b6105cf8989868181106105b5576105b5613dd8565b90506020020160208101906105ca9190613c14565b61195a565b6119e1565b806105de81613e00565b915050610530565b505050505050565b5f6105fc8686868686611aa8565b9695505050505050565b61060e611c04565b5f5b8181101561068457600283838381811061062c5761062c613dd8565b90506020020160208101906106419190613c14565b81546001810183555f928352602090922090910180546001600160a01b0319166001600160a01b039092169190911790558061067c81613e00565b915050610610565b505050565b5f6106978989898989611aa8565b6040805160018082528183019092529192505f91906020808301908036833701905050905081815f815181106106cf576106cf613dd8565b6020908102919091010152604051633d318f9560e21b81526001600160a01b0386169063f4c63e549061070c908790879086903390600401613d81565b5f604051808303815f87803b158015610723575f80fd5b505af1158015610735573d5f803e3d5ffd5b505050505098975050505050505050565b60606107568a8a8a8a8a8a611c30565b604051633d318f9560e21b81529091506001600160a01b0385169063f4c63e549061078b908690869086903390600401613d81565b5f604051808303815f87803b1580156107a2575f80fd5b505af11580156107b4573d5f803e3d5ffd5b505050509998505050505050505050565b6107cd611c04565b6107d65f611e84565b565b60015433906001600160a01b031681146108105760405163118cdaa760e01b81526001600160a01b03821660048201526024016104d6565b61081981611e84565b50565b6040805160e0810182525f80825260208201819052918101829052606081018290526080810182905260a0810182905260c08101829052604080516060810182525f80825260208201819052918101919091525f365f6108e7565b5f80823560f01c8080156108be57600181146108c9577f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5acc82015493506002850192506108e0565b6002850192506108e0565b6001600160a01b03853560501c1693506016850192505b5050915091565b5f60046108f381610877565b9150915081875261090381610877565b606089018290529092506001810191503560f81c8015610932578135600882602003021c602089015280820191505b50803560f81c6001820191508135600882602003021c8060808a01528183019250823560e81c915062ffffff8262ffffff0382020460a08a0152505060038101905061097d81610877565b9150945061098a81610877565b909250905081610998578491505b8160408801526109a781610877565b60c089810192909252803590911c8752600881013560f81c9250600901905081156109e757803560c01c6020870152600881013560601c6040870152601c015b6001810193503560f81c6020029150610a0590508583838688611aa8565b9550505050505090565b60605f8180600660043560f890811c90600535901c8167ffffffffffffffff811115610a3d57610a3d61318c565b604051908082528060200260200182016040528015610a8657816020015b604080516060810182525f80825260208083018290529282015282525f19909201910181610a5b5790505b5094508067ffffffffffffffff811115610aa257610aa261318c565b604051908082528060200260200182016040528015610af257816020015b604080516080810182525f8082526020808301829052928201819052606082015282525f19909201910181610ac05790505b509350610afe83610877565b9096506003810193503560e81c5f80805b85811015610b7c576020808202018901519150610b2b87610877565b8184526001810198509093503560f81c8015610b56578735600882602003021c602084015280880197505b50610b6087610877565b9750925082610b6d578992505b60408201839052600101610b0f565b505f5b84811015610bf2576020808202018801519150610b9b87610877565b97509250828252863560f81c6001880197508735600882602003021c806020850152818901985062ffffff8662ffffff0382020460408501525050610bdf87610877565b6060840182905297509250600101610b7f565b5050604080516060810182525f80825260208201819052918101919091529350610c1b92505050565b813560c01c8152600982019136905f906008013560f81c8015610c5657843560c01c6020850152600885013560601c6040850152601c909401935b505050600182016020833560f81c02610c73868684848b88611c30565b97505050505050505090565b6003546060906001600160a01b0316331480610ca457505f546001600160a01b031633145b610cf05760405162461bcd60e51b815260206004820152601360248201527f41646472657373206e6f7420616c6c6f7765640000000000000000000000000060448201526064016104d6565b5f865167ffffffffffffffff811115610d0b57610d0b61318c565b604051908082528060200260200182016040528015610d34578160200160208202803683370190505b5090505f875167ffffffffffffffff811115610d5257610d5261318c565b604051908082528060200260200182016040528015610d7b578160200160208202803683370190505b5090505f5b8851811015610ecb57888181518110610d9b57610d9b613dd8565b60200260200101515f0151828281518110610db857610db8613dd8565b60200260200101906001600160a01b031690816001600160a01b031681525050888181518110610dea57610dea613dd8565b6020026020010151602001515f14610e1f57888181518110610e0e57610e0e613dd8565b602002602001015160200151610e41565b610e41828281518110610e3457610e34613dd8565b602002602001015161195a565b838281518110610e5357610e53613dd8565b602002602001018181525050610eb9828281518110610e7457610e74613dd8565b60200260200101518a8381518110610e8e57610e8e613dd8565b602002602001015160400151858481518110610eac57610eac613dd8565b60200260200101516119e1565b80610ec381613e00565b915050610d80565b505f875167ffffffffffffffff811115610ee757610ee761318c565b604051908082528060200260200182016040528015610f10578160200160208202803683370190505b5090505f885167ffffffffffffffff811115610f2e57610f2e61318c565b604051908082528060200260200182016040528015610f57578160200160208202803683370190505b5090505f5b8951811015610ff857898181518110610f7757610f77613dd8565b60200260200101515f0151828281518110610f9457610f94613dd8565b60200260200101906001600160a01b031690816001600160a01b031681525050610fc9828281518110610e3457610e34613dd8565b838281518110610fdb57610fdb613dd8565b602090810291909101015280610ff081613e00565b915050610f5c565b5060405163cb70e27360e01b81526001600160a01b0387169063cb70e273905f9061102d908c908c908a903390600401613d81565b5f604051808303818588803b158015611044575f80fd5b505af1158015611056573d5f803e3d5ffd5b5050505050885167ffffffffffffffff8111156110755761107561318c565b60405190808252806020026020018201604052801561109e578160200160208202803683370190505b5094505f5b8951811015611228578281815181106110be576110be613dd8565b60200260200101516110db838381518110610e3457610e34613dd8565b6110e59190613e18565b8682815181106110f7576110f7613dd8565b60200260200101818152505089818151811061111557611115613dd8565b60200260200101516040015186828151811061113357611133613dd8565b602002602001015110156111895760405162461bcd60e51b815260206004820152601760248201527f536c697070616765204c696d697420457863656564656400000000000000000060448201526064016104d6565b6112168a828151811061119e5761119e613dd8565b60200260200101515f01515f6001600160a01b03168c84815181106111c5576111c5613dd8565b6020026020010151606001516001600160a01b031614611202578b83815181106111f1576111f1613dd8565b602002602001015160600151611204565b335b888481518110610eac57610eac613dd8565b8061122081613e00565b9150506110a3565b507f2c96555a96d94780f3a97aeb724514e80e331842f3143742d85da5aa68df9d3033858588858e5167ffffffffffffffff8111156112695761126961318c565b604051908082528060200260200182016040528015611292578160200160208202803683370190505b505f805f6040516112ab99989796959493929190613e62565b60405180910390a15050505095945050505050565b600281815481106112cf575f80fd5b5f918252602090912001546001600160a01b0316905081565b5f6112f7878787878787611e9d565b979650505050505050565b61130a611c04565b600380546001600160a01b0319166001600160a01b0383169081179091556040517f1535fa8f7275b71050af30bf7f74391b45be7ba2b545fd28279dafe9b50f6424905f90a250565b5f6113628a8a8a8a8a8a611e9d565b6040805160018082528183019092529192505f91906020808301908036833701905050905081815f8151811061139a5761139a613dd8565b6020908102919091010152604051633d318f9560e21b81526001600160a01b0386169063f4c63e54906113d7908790879086903390600401613d81565b5f604051808303815f87803b1580156113ee575f80fd5b505af1158015611400573d5f803e3d5ffd5b50505050509998505050505050505050565b61141a611c04565b600180546001600160a01b0383166001600160a01b0319909116811790915561144a5f546001600160a01b031690565b6001600160a01b03167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270060405160405180910390a350565b60606112f7878787878787611c30565b60606114b66040518060600160405280606081526020015f81526020015f81525090565b60605f8034116114c75789516114d5565b60018a516114d59190613e18565b905060405180606001604052808267ffffffffffffffff8111156114fb576114fb61318c565b60405190808252806020026020018201604052801561153f57816020015b604080518082019091525f80825260208201528152602001906001900390816115195790505b5081526020018c6020015181526020018c6040015181525092508067ffffffffffffffff8111156115725761157261318c565b6040519080825280602002602001820160405280156115b657816020015b604080518082019091525f80825260208201528152602001906001900390816115905790505b509150505f805b8a5181101561187c575f6001600160a01b03168b82815181106115e2576115e2613dd8565b60200260200101515f01516001600160a01b031603611668578a818151811061160d5761160d613dd8565b6020026020010151602001515f0361164357348b828151811061163257611632613dd8565b602002602001015160200181815250505b8a818151811061165557611655613dd8565b602002602001015160200151915061186a565b8a818151811061167a5761167a613dd8565b6020026020010151602001515f03611734578a818151811061169e5761169e613dd8565b6020908102919091010151516040516370a0823160e01b81523360048201526001600160a01b03909116906370a0823190602401602060405180830381865afa1580156116ed573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906117119190613f07565b8b828151811061172357611723613dd8565b602002602001015160200181815250505b5f821561174b57611746600183613e18565b61174d565b815b90508b828151811061176157611761613dd8565b60200260200101515f0151855f0151828151811061178157611781613dd8565b60209081029190910101516001600160a01b0390911690528b518c90839081106117ad576117ad613dd8565b602002602001015160200151855f015182815181106117ce576117ce613dd8565b602002602001015160200181815250508b82815181106117f0576117f0613dd8565b60200260200101516040015184828151811061180e5761180e613dd8565b60209081029190910101516001600160a01b0390911690528b518c908390811061183a5761183a613dd8565b60200260200101516020015184828151811061185857611858613dd8565b60200260200101516020018181525050505b8061187481613e00565b9150506115bd565b508034146118be5760405162461bcd60e51b815260206004820152600f60248201526e57726f6e67206d73672e76616c756560881b60448201526064016104d6565b50895160608b01516040517fedd9444b0000000000000000000000000000000000000000000000000000000081526001600160a01b039092169163edd9444b916119119186918691339190600401613fac565b5f604051808303815f87803b158015611928575f80fd5b505af115801561193a573d5f803e3d5ffd5b5050505061194c898989898989611f72565b9a9950505050505050505050565b5f6001600160a01b038216611970575047919050565b6040516370a0823160e01b81523060048201526001600160a01b038316906370a0823190602401602060405180830381865afa1580156119b2573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906119d69190613f07565b92915050565b919050565b6001600160a01b038316611a94575f826001600160a01b0316826040515f6040518083038185875af1925050503d805f8114611a38576040519150601f19603f3d011682016040523d82523d5f602084013e611a3d565b606091505b5050905080611a8e5760405162461bcd60e51b815260206004820152601360248201527f455448207472616e73666572206661696c65640000000000000000000000000060448201526064016104d6565b50505050565b6106846001600160a01b0384168383612b02565b84515f906001600160a01b0316611b195785602001515f03611acf57346020870152611bf7565b85602001513414611b145760405162461bcd60e51b815260206004820152600f60248201526e57726f6e67206d73672e76616c756560881b60448201526064016104d6565b611bf7565b3415611b595760405162461bcd60e51b815260206004820152600f60248201526e57726f6e67206d73672e76616c756560881b60448201526064016104d6565b85602001515f03611bd35785516040516370a0823160e01b81523360048201526001600160a01b03909116906370a0823190602401602060405180830381865afa158015611ba9573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611bcd9190613f07565b60208701525b604086015160208701518751611bf7926001600160a01b0390911691339190612b76565b6105fc8686868686612baf565b5f546001600160a01b031633146107d65760405163118cdaa760e01b81523360048201526024016104d6565b60605f805b8851811015611e34575f6001600160a01b0316898281518110611c5a57611c5a613dd8565b60200260200101515f01516001600160a01b031603611ce057888181518110611c8557611c85613dd8565b6020026020010151602001515f03611cbb5734898281518110611caa57611caa613dd8565b602002602001015160200181815250505b888181518110611ccd57611ccd613dd8565b6020026020010151602001519150611e22565b888181518110611cf257611cf2613dd8565b6020026020010151602001515f03611dac57888181518110611d1657611d16613dd8565b6020908102919091010151516040516370a0823160e01b81523360048201526001600160a01b03909116906370a0823190602401602060405180830381865afa158015611d65573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611d899190613f07565b898281518110611d9b57611d9b613dd8565b602002602001015160200181815250505b611e22338a8381518110611dc257611dc2613dd8565b6020026020010151604001518b8481518110611de057611de0613dd8565b6020026020010151602001518c8581518110611dfe57611dfe613dd8565b60200260200101515f01516001600160a01b0316612b76909392919063ffffffff16565b80611e2c81613e00565b915050611c35565b50803414611e765760405162461bcd60e51b815260206004820152600f60248201526e57726f6e67206d73672e76616c756560881b60448201526064016104d6565b610460888888888888611f72565b600180546001600160a01b0319169055610819816130b8565b85516040805160a08101825287516001600160a01b0390811660608084019182526020808c0180516080870152928552808d015181860152858d015185870152855180870187528c870151851681529251908301528b015193517f30f28b7a0000000000000000000000000000000000000000000000000000000081525f9592909216936330f28b7a93611f38939092913391600401614060565b5f604051808303815f87803b158015611f4f575f80fd5b505af1158015611f61573d5f803e3d5ffd5b505050506112f78686868686612baf565b60605f875167ffffffffffffffff811115611f8f57611f8f61318c565b604051908082528060200260200182016040528015611fb8578160200160208202803683370190505b5090505f885167ffffffffffffffff811115611fd657611fd661318c565b604051908082528060200260200182016040528015611fff578160200160208202803683370190505b5090505f5b89518110156122175789818151811061201f5761201f613dd8565b60200260200101516020015183828151811061203d5761203d613dd8565b60200260200101818152505089818151811061205b5761205b613dd8565b60200260200101515f015182828151811061207857612078613dd8565b60200260200101906001600160a01b031690816001600160a01b0316815250505f5b8181101561214d578a81815181106120b4576120b4613dd8565b60200260200101515f01516001600160a01b03168b83815181106120da576120da613dd8565b60200260200101515f01516001600160a01b03160361213b5760405162461bcd60e51b815260206004820152601760248201527f4475706c696361746520736f7572636520746f6b656e7300000000000000000060448201526064016104d6565b8061214581613e00565b91505061209a565b505f5b89518110156122045789818151811061216b5761216b613dd8565b60200260200101515f01516001600160a01b03168b838151811061219157612191613dd8565b60200260200101515f01516001600160a01b0316036121f25760405162461bcd60e51b815260206004820152601760248201527f417262697472616765206e6f7420737570706f7274656400000000000000000060448201526064016104d6565b806121fc81613e00565b915050612150565b508061220f81613e00565b915050612004565b505f885167ffffffffffffffff8111156122335761223361318c565b60405190808252806020026020018201604052801561225c578160200160208202803683370190505b5090505f5b895181101561246a5789818151811061227c5761227c613dd8565b6020026020010151602001518a828151811061229a5761229a613dd8565b60200260200101516040015111156122f45760405162461bcd60e51b815260206004820152601a60248201527f4d696e696d756d2067726561746572207468616e2071756f746500000000000060448201526064016104d6565b5f8a828151811061230757612307613dd8565b602002602001015160400151116123605760405162461bcd60e51b815260206004820152601660248201527f4d696e696d756d206f7574707574206973207a65726f0000000000000000000060448201526064016104d6565b5f5b81811015612415578a818151811061237c5761237c613dd8565b60200260200101515f01516001600160a01b03168b83815181106123a2576123a2613dd8565b60200260200101515f01516001600160a01b0316036124035760405162461bcd60e51b815260206004820152601c60248201527f4475706c69636174652064657374696e6174696f6e20746f6b656e730000000060448201526064016104d6565b8061240d81613e00565b915050612362565b5061243b8a828151811061242b5761242b613dd8565b60200260200101515f015161195a565b82828151811061244d5761244d613dd8565b60209081029190910101528061246281613e00565b915050612261565b5060405163cb70e27360e01b81526001600160a01b0387169063cb70e27390349061249f908c908c9089903390600401613d81565b5f604051808303818588803b1580156124b6575f80fd5b505af11580156124c8573d5f803e3d5ffd5b50505050505f895167ffffffffffffffff8111156124e8576124e861318c565b604051908082528060200260200182016040528015612511578160200160208202803683370190505b509050895167ffffffffffffffff81111561252e5761252e61318c565b604051908082528060200260200182016040528015612557578160200160208202803683370190505b50865190955060201c61ffff165f8190036125715750611f405b6127108111156125c35760405162461bcd60e51b815260206004820152601060248201527f496e76616c69642052656620436f64650000000000000000000000000000000060448201526064016104d6565b5f5b8b518110156129f7578381815181106125e0576125e0613dd8565b60200260200101516125fd8d838151811061242b5761242b613dd8565b6126079190613e18565b87828151811061261957612619613dd8565b6020026020010181815250505f886020015167ffffffffffffffff1611156128035760408801516001600160a01b03166126955760405162461bcd60e51b815260206004820152601260248201527f4e756c6c2066656520726563697069656e74000000000000000000000000000060448201526064016104d6565b6126a86032670de0b6b3a76400006140d7565b886020015167ffffffffffffffff1611156126f45760405162461bcd60e51b815260206004820152600c60248201526b08ccaca40e8dede40d0d2ced60a31b60448201526064016104d6565b60408801516001600160a01b0316301461278b5761278b8c828151811061271d5761271d613dd8565b60200260200101515f01518960400151670de0b6b3a764000061271061274391906140f6565b858c6020015167ffffffffffffffff168c878151811061276557612765613dd8565b602002602001015161277791906140f6565b61278191906140f6565b6105cf91906140d7565b670de0b6b3a7640000886020015167ffffffffffffffff16670de0b6b3a76400006127b69190613e18565b8883815181106127c8576127c8613dd8565b60200260200101516127da91906140f6565b6127e491906140d7565b8782815181106127f6576127f6613dd8565b6020026020010181815250505b8b818151811061281557612815613dd8565b60200260200101516020015187828151811061283357612833613dd8565b6020026020010151612845919061410d565b83828151811061285757612857613dd8565b6020026020010181815250505f83828151811061287657612876613dd8565b60200260200101511380156128905750875160301c600116155b156128d2578b81815181106128a7576128a7613dd8565b6020026020010151602001518782815181106128c5576128c5613dd8565b6020026020010181815250505b8b81815181106128e4576128e4613dd8565b60200260200101516040015187828151811061290257612902613dd8565b602002602001015110156129585760405162461bcd60e51b815260206004820152601760248201527f536c697070616765204c696d697420457863656564656400000000000000000060448201526064016104d6565b6129e58c828151811061296d5761296d613dd8565b60200260200101515f01515f6001600160a01b03168e848151811061299457612994613dd8565b6020026020010151606001516001600160a01b0316146129d1578d83815181106129c0576129c0613dd8565b6020026020010151606001516129d3565b335b898481518110610eac57610eac613dd8565b806129ef81613e00565b9150506125c5565b50505f8a5167ffffffffffffffff811115612a1457612a1461318c565b604051908082528060200260200182016040528015612a3d578160200160208202803683370190505b5090505f5b8b51811015612aa4578b8181518110612a5d57612a5d613dd8565b60200260200101515f0151828281518110612a7a57612a7a613dd8565b6001600160a01b039092166020928302919091019091015280612a9c81613e00565b915050612a42565b50865160208801516040808a015190517f2c96555a96d94780f3a97aeb724514e80e331842f3143742d85da5aa68df9d3093612aeb9333938b938b938e938a938c93613e62565b60405180910390a150505050509695505050505050565b6040516001600160a01b0383811660248301526044820183905261068491859182169063a9059cbb906064015b604051602081830303815290604052915060e01b6020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050613107565b6040516001600160a01b038481166024830152838116604483015260648201839052611a8e9186918216906323b872dd90608401612b2f565b5f85608001518660a001511115612c085760405162461bcd60e51b815260206004820152601a60248201527f4d696e696d756d2067726561746572207468616e2071756f746500000000000060448201526064016104d6565b5f8660a0015111612c5b5760405162461bcd60e51b815260206004820152601660248201527f4d696e696d756d206f7574707574206973207a65726f0000000000000000000060448201526064016104d6565b85606001516001600160a01b0316865f01516001600160a01b031603612cc35760405162461bcd60e51b815260206004820152601760248201527f417262697472616765206e6f7420737570706f7274656400000000000000000060448201526064016104d6565b5f612cd1876060015161195a565b6040805160018082528183019092529192505f9190602080830190803683370190505090508760200151815f81518110612d0d57612d0d613dd8565b602090810291909101015260405163cb70e27360e01b81526001600160a01b0386169063cb70e273903490612d4c908b908b9087903390600401613d81565b5f604051808303818588803b158015612d63575f80fd5b505af1158015612d75573d5f803e3d5ffd5b505050505081612d88896060015161195a565b612d929190613e18565b602085015190935067ffffffffffffffff1615612f5f5760408401516001600160a01b0316612e035760405162461bcd60e51b815260206004820152601260248201527f4e756c6c2066656520726563697069656e74000000000000000000000000000060448201526064016104d6565b612e166032670de0b6b3a76400006140d7565b846020015167ffffffffffffffff161115612e625760405162461bcd60e51b815260206004820152600c60248201526b08ccaca40e8dede40d0d2ced60a31b60448201526064016104d6565b835160201c61ffff165f819003612e785750611f405b612710811115612eca5760405162461bcd60e51b815260206004820152601060248201527f496e76616c69642052656620436f64650000000000000000000000000000000060448201526064016104d6565b60408501516001600160a01b03163014612f1c57612f1c89606001518660400151670de0b6b3a7640000612710612f0191906140f6565b84896020015167ffffffffffffffff168961277791906140f6565b670de0b6b3a7640000856020015167ffffffffffffffff16670de0b6b3a7640000612f479190613e18565b612f5190866140f6565b612f5b91906140d7565b9350505b5f886080015184612f70919061410d565b90505f81138015612f865750845160301c600116155b15612f9357886080015193505b8860a00151841015612fe75760405162461bcd60e51b815260206004820152601760248201527f536c697070616765204c696d697420457863656564656400000000000000000060448201526064016104d6565b606089015160c08a015161301691906001600160a01b03161561300e578a60c00151613010565b335b866119e1565b6020808a01518a516060808d015189518a8601516040808d01518151338152988901979097526001600160a01b03958616908801529286018a9052908316608086015260a0850186905267ffffffffffffffff90811660c08601521660e0840152166101008201527f69db20ca9e32403e6c56e5193b3e3b2827ae5c430ccfdea392ba950d2d1ab2bc906101200160405180910390a150505095945050505050565b5f80546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b5f8060205f8451602086015f885af180613126576040513d5f823e3d81fd5b50505f513d9150811561313d57806001141561314a565b6001600160a01b0384163b155b15611a8e576040517f5274afe70000000000000000000000000000000000000000000000000000000081526001600160a01b03851660048201526024016104d6565b634e487b7160e01b5f52604160045260245ffd5b6040516080810167ffffffffffffffff811182821017156131c3576131c361318c565b60405290565b6040516060810167ffffffffffffffff811182821017156131c3576131c361318c565b604051601f8201601f1916810167ffffffffffffffff811182821017156132155761321561318c565b604052919050565b80356001600160a01b03811681146119dc575f80fd5b5f60808284031215613243575f80fd5b61324b6131a0565b90506132568261321d565b81526020808301358183015260408301356040830152606083013567ffffffffffffffff80821115613286575f80fd5b818501915085601f830112613299575f80fd5b8135818111156132ab576132ab61318c565b6132bd601f8201601f191685016131ec565b915080825286848285010111156132d2575f80fd5b80848401858401375f8482840101525080606085015250505092915050565b5f67ffffffffffffffff82111561330a5761330a61318c565b5060051b60200190565b5f82601f830112613323575f80fd5b81356020613338613333836132f1565b6131ec565b82815260609283028501820192828201919087851115613356575f80fd5b8387015b858110156133aa5781818a031215613371575f8081fd5b6133796131c9565b6133828261321d565b81528582013586820152604061339981840161321d565b90820152845292840192810161335a565b5090979650505050505050565b5f82601f8301126133c6575f80fd5b813560206133d6613333836132f1565b82815260079290921b840181019181810190868411156133f4575f80fd5b8286015b848110156134545760808189031215613410575f8081fd5b6134186131a0565b6134218261321d565b8152818501358582015260408083013590820152606061344281840161321d565b908201528352918301916080016133f8565b509695505050505050565b5f8083601f84011261346f575f80fd5b50813567ffffffffffffffff811115613486575f80fd5b60208301915083602082850101111561349d575f80fd5b9250929050565b803567ffffffffffffffff811681146119dc575f80fd5b5f606082840312156134cb575f80fd5b6134d36131c9565b90506134de826134a4565b81526134ec602083016134a4565b60208201526134fd6040830161321d565b604082015292915050565b5f805f805f805f805f806101408b8d031215613522575f80fd5b8a3567ffffffffffffffff80821115613539575f80fd5b6135458e838f01613233565b9b5060208d013591508082111561355a575f80fd5b6135668e838f01613314565b9a5060408d013591508082111561357b575f80fd5b6135878e838f016133b7565b995060608d013591508082111561359c575f80fd5b6135a88e838f0161345f565b90995097508791506135bc60808e0161321d565b96506135cb8e60a08f016134bb565b95506135da6101008e0161321d565b94506101208d01359150808211156135f0575f80fd5b506135fd8d828e0161345f565b915080935050809150509295989b9194979a5092959850565b5f8151808452602080850194508084015f5b8381101561364457815187529582019590820190600101613628565b509495945050505050565b602081525f6136616020830184613616565b9392505050565b5f805f805f805f610100888a03121561367f575f80fd5b873567ffffffffffffffff80821115613696575f80fd5b6136a28b838c01613233565b985060208a01359150808211156136b7575f80fd5b6136c38b838c01613314565b975060408a01359150808211156136d8575f80fd5b6136e48b838c016133b7565b965060608a01359150808211156136f9575f80fd5b506137068a828b0161345f565b909550935061371990506080890161321d565b91506137288960a08a016134bb565b905092959891949750929550565b5f8083601f840112613746575f80fd5b50813567ffffffffffffffff81111561375d575f80fd5b6020830191508360208260051b850101111561349d575f80fd5b5f805f805f6060868803121561378b575f80fd5b853567ffffffffffffffff808211156137a2575f80fd5b6137ae89838a01613736565b909750955060208801359150808211156137c6575f80fd5b506137d388828901613736565b90945092506137e690506040870161321d565b90509295509295909350565b5f60e08284031215613802575f80fd5b60405160e0810181811067ffffffffffffffff821117156138255761382561318c565b6040529050806138348361321d565b81526020830135602082015261384c6040840161321d565b604082015261385d6060840161321d565b60608201526080830135608082015260a083013560a082015261388260c0840161321d565b60c08201525092915050565b5f805f805f61018086880312156138a3575f80fd5b6138ad87876137f2565b945060e086013567ffffffffffffffff8111156138c8575f80fd5b6138d48882890161345f565b90955093506138e89050610100870161321d565b91506137e68761012088016134bb565b5f8060208385031215613909575f80fd5b823567ffffffffffffffff81111561391f575f80fd5b61392b85828601613736565b90969095509350505050565b5f805f805f805f806101c0898b03121561394f575f80fd5b6139598a8a6137f2565b975060e089013567ffffffffffffffff80821115613975575f80fd5b6139818c838d0161345f565b90995097508791506139966101008c0161321d565b96506139a68c6101208d016134bb565b95506139b56101808c0161321d565b94506101a08b01359150808211156139cb575f80fd5b506139d88b828c0161345f565b999c989b5096995094979396929594505050565b5f805f805f805f805f6101208a8c031215613a05575f80fd5b893567ffffffffffffffff80821115613a1c575f80fd5b613a288d838e01613314565b9a5060208c0135915080821115613a3d575f80fd5b613a498d838e016133b7565b995060408c0135915080821115613a5e575f80fd5b613a6a8d838e0161345f565b9099509750879150613a7e60608d0161321d565b9650613a8d8d60808e016134bb565b9550613a9b60e08d0161321d565b94506101008c0135915080821115613ab1575f80fd5b50613abe8c828d0161345f565b915080935050809150509295985092959850929598565b5f805f805f60808688031215613ae9575f80fd5b853567ffffffffffffffff80821115613b00575f80fd5b613b0c89838a01613314565b96506020880135915080821115613b21575f80fd5b613b2d89838a016133b7565b95506040880135915080821115613b42575f80fd5b50613b4f8882890161345f565b90945092506137e690506060870161321d565b5f60208284031215613b72575f80fd5b5035919050565b5f805f805f806101a08789031215613b8f575f80fd5b863567ffffffffffffffff80821115613ba6575f80fd5b613bb28a838b01613233565b9750613bc18a60208b016137f2565b9650610100890135915080821115613bd7575f80fd5b50613be489828a0161345f565b9095509350613bf89050610120880161321d565b9150613c088861014089016134bb565b90509295509295509295565b5f60208284031215613c24575f80fd5b6136618261321d565b5f805f805f805f805f6101e08a8c031215613c46575f80fd5b893567ffffffffffffffff80821115613c5d575f80fd5b613c698d838e01613233565b9a50613c788d60208e016137f2565b99506101008c0135915080821115613c8e575f80fd5b613c9a8d838e0161345f565b9099509750879150613caf6101208d0161321d565b9650613cbf8d6101408e016134bb565b9550613cce6101a08d0161321d565b94506101c08c0135915080821115613ab1575f80fd5b5f805f805f8060e08789031215613cf9575f80fd5b863567ffffffffffffffff80821115613d10575f80fd5b613d1c8a838b01613314565b97506020890135915080821115613d31575f80fd5b613d3d8a838b016133b7565b96506040890135915080821115613d52575f80fd5b50613d5f89828a0161345f565b9095509350613d7290506060880161321d565b9150613c0888608089016134bb565b60608152836060820152838560808301375f608085830101525f601f19601f86011682016080838203016020840152613dbd6080820186613616565b9150506001600160a01b038316604083015295945050505050565b634e487b7160e01b5f52603260045260245ffd5b634e487b7160e01b5f52601160045260245ffd5b5f60018201613e1157613e11613dec565b5060010190565b818103818111156119d6576119d6613dec565b5f8151808452602080850194508084015f5b838110156136445781516001600160a01b031687529582019590820190600101613e3d565b5f6101206001600160a01b03808d168452816020850152613e858285018d613616565b91508382036040850152613e99828c613e2b565b91508382036060850152613ead828b613616565b91508382036080850152613ec1828a613e2b565b915083820360a0850152613ed58289613616565b67ffffffffffffffff97881660c08601529590961660e084015250509216610100909201919091529695505050505050565b5f60208284031215613f17575f80fd5b5051919050565b5f8151808452602080850194508084015f5b8381101561364457613f5687835180516001600160a01b03168252602090810151910152565b6040969096019590820190600101613f30565b5f81518084525f5b81811015613f8d57602081850181015186830182015201613f71565b505f602082860101526020601f19601f83011685010191505092915050565b608081525f60e08201865160606080850152818151808452610100860191506020935083830192505f5b8181101561400f57613ffc83855180516001600160a01b03168252602090810151910152565b9284019260409290920191600101613fd6565b50508289015160a0860152604089015160c0860152848103838601526140358189613f1e565b9250505061404e60408401866001600160a01b03169052565b82810360608401526112f78185613f69565b5f61010061408283885180516001600160a01b03168252602090810151910152565b60208701516040840152604087015160608401526140b6608084018780516001600160a01b03168252602090810151910152565b6001600160a01b03851660c08401528060e08401526112f781840185613f69565b5f826140f157634e487b7160e01b5f52601260045260245ffd5b500490565b80820281158282048414176119d6576119d6613dec565b8181035f83128015838313168383128216171561412c5761412c613dec565b509291505056fea26469706673582212201f12ee48d2d74a83b6ae0555a0c64d60e509b162a6d823cd98185fee561d0a3364736f6c63430008140033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000000636843c30b6b10d3dc9af803e7a7956aa994c
-----Decoded View---------------
Arg [0] : owner (address): 0x000636843C30b6B10d3DC9aF803E7A7956aa994C
-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 000000000000000000000000000636843c30b6b10d3dc9af803e7a7956aa994c
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in FRAX
0
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ 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.