Feature Tip: Add private address tag to any address under My Name Tag !
Source Code
Latest 25 from a total of 283 transactions
| Transaction Hash |
Method
|
Block
|
From
|
|
To
|
||||
|---|---|---|---|---|---|---|---|---|---|
| Transfer | 24261817 | 34 days ago | IN | 1 wei | 0.0000006 | ||||
| Transfer | 24261811 | 34 days ago | IN | 8 wei | 0.00000068 | ||||
| Transfer | 24261801 | 34 days ago | IN | 1 wei | 0.00000067 | ||||
| Transfer | 24261793 | 34 days ago | IN | 1 wei | 0.00000081 | ||||
| Transfer | 24261781 | 34 days ago | IN | 4 wei | 0.00000071 | ||||
| Transfer | 24261770 | 34 days ago | IN | 9 wei | 0.00000071 | ||||
| Transfer | 24261770 | 34 days ago | IN | 5 wei | 0.00000071 | ||||
| Transfer | 24261761 | 34 days ago | IN | 2 wei | 0.00000068 | ||||
| Transfer | 24261758 | 34 days ago | IN | 3 wei | 0.00000069 | ||||
| Transfer | 24261719 | 34 days ago | IN | 8 wei | 0.00000062 | ||||
| Transfer | 24261719 | 34 days ago | IN | 8 wei | 0.00000062 | ||||
| Transfer | 24261715 | 34 days ago | IN | 3 wei | 0.00000068 | ||||
| Transfer | 24261711 | 34 days ago | IN | 5 wei | 0.00000069 | ||||
| Transfer | 24261693 | 34 days ago | IN | 1 wei | 0.00000067 | ||||
| Transfer | 24261685 | 34 days ago | IN | 2 wei | 0.00000064 | ||||
| Transfer | 24261654 | 34 days ago | IN | 8 wei | 0.0000007 | ||||
| Transfer | 24261648 | 34 days ago | IN | 3 wei | 0.00000072 | ||||
| Transfer | 24261638 | 34 days ago | IN | 2 wei | 0.00000069 | ||||
| Transfer | 24261617 | 34 days ago | IN | 9 wei | 0.00000071 | ||||
| Transfer | 24261617 | 34 days ago | IN | 7 wei | 0.00000071 | ||||
| Transfer | 24261608 | 34 days ago | IN | 4 wei | 0.00000067 | ||||
| Transfer | 24261605 | 34 days ago | IN | 1 wei | 0.00000066 | ||||
| Transfer | 24261605 | 34 days ago | IN | 1 wei | 0.00000066 | ||||
| Transfer | 24261599 | 34 days ago | IN | 5 wei | 0.00000067 | ||||
| Transfer | 24261580 | 34 days ago | IN | 7 wei | 0.00000068 |
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Name:
FluidDexLite
Compiler Version
v0.8.29+commit.ab55807c
Optimization Enabled:
Yes with 10000000 runs
Other Settings:
cancun EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.29;
import "./coreInternals.sol";
/// @title FluidDexLite
contract FluidDexLite is CoreInternals {
constructor(address auth_, address liquidity_, address deployerContract_) {
_isAuth[auth_] = 1;
LIQUIDITY = IFluidLiquidity(liquidity_);
DEPLOYER_CONTRACT = deployerContract_;
}
/// @notice Swap through a single dex pool
/// @dev Uses _swapIn for positive amountSpecified_ (user provides input), _swapOut for negative (user receives output).
/// @param dexKey_ The dex pool to swap through.
/// @param swap0To1_ Whether to swap from token0 to token1 or vice versa.
/// @param amountSpecified_ The amount to swap (positive for exact input, negative for exact output).
/// @param amountLimit_ The minimum/maximum amount for the unspecified side.
/// @param to_ The recipient address.
function swapSingle(
DexKey calldata dexKey_,
bool swap0To1_,
int256 amountSpecified_,
uint256 amountLimit_,
address to_,
bool isCallback_,
bytes calldata callbackData_,
bytes calldata extraData_
) external payable _reentrancyLock returns (uint256 amountUnspecified_) {
if (amountSpecified_ > 0) {
amountUnspecified_ = _swapIn(dexKey_, swap0To1_, uint256(amountSpecified_));
if (amountUnspecified_ < amountLimit_) {
revert AmountLimitNotMet(amountUnspecified_, amountLimit_);
}
if (extraData_.length == 0) {
if (swap0To1_) {
_transferTokens(dexKey_.token0, uint256(amountSpecified_), dexKey_.token1, amountUnspecified_, to_, isCallback_, callbackData_);
} else {
_transferTokens(dexKey_.token1, uint256(amountSpecified_), dexKey_.token0, amountUnspecified_, to_, isCallback_, callbackData_);
}
} else if (bytes32(extraData_) == ESTIMATE_SWAP) {
revert EstimateSwap(amountUnspecified_);
} else {
_callExtraDataSlot(
abi.encode(
SWAP_SINGLE,
abi.encode(dexKey_, swap0To1_, amountSpecified_, amountUnspecified_, extraData_)
)
);
}
} else {
amountUnspecified_ = _swapOut(dexKey_, swap0To1_, uint256(-amountSpecified_));
if (amountUnspecified_ > amountLimit_) {
revert AmountLimitExceeded(amountUnspecified_, amountLimit_);
}
if (extraData_.length == 0) {
if (swap0To1_) {
_transferTokens(dexKey_.token0, amountUnspecified_, dexKey_.token1, uint256(-amountSpecified_), to_, isCallback_, callbackData_);
} else {
_transferTokens(dexKey_.token1, amountUnspecified_, dexKey_.token0, uint256(-amountSpecified_), to_, isCallback_, callbackData_);
}
} else if (bytes32(extraData_) == ESTIMATE_SWAP) {
revert EstimateSwap(amountUnspecified_);
} else {
_callExtraDataSlot(
abi.encode(
SWAP_SINGLE,
abi.encode(dexKey_, swap0To1_, amountSpecified_, amountUnspecified_, extraData_)
)
);
}
}
}
/// @notice Swap through a path of dex pools
/// @dev Uses _swapIn for positive amountSpecified_ (user provides input), _swapOut for negative (user receives output).
/// @param path_ The path of the swap.
/// @param dexKeys_ The dex pools to swap through.
/// @param amountSpecified_ The amount to swap (positive for exact input, negative for exact output).
/// @param amountLimits_ The minimum/maximum amount for the unspecified side for all swaps.
/// @param transferParams_ The parameters for the transfer.
/// @return amountUnspecified_ The amount of the unspecified token.
function swapHop(
address[] calldata path_,
DexKey[] calldata dexKeys_,
int256 amountSpecified_,
uint256[] calldata amountLimits_,
TransferParams calldata transferParams_
) external payable _reentrancyLock returns (uint256 amountUnspecified_) {
if (dexKeys_.length == 0) {
revert EmptyDexKeysArray();
}
if (path_.length - 1 != dexKeys_.length) {
revert InvalidPathLength(path_.length, dexKeys_.length);
}
if (amountLimits_.length != dexKeys_.length) {
revert InvalidAmountLimitsLength(amountLimits_.length, dexKeys_.length);
}
if (amountSpecified_ > 0) {
// Swap In (Exact input amount provided by the user)
amountUnspecified_ = uint256(amountSpecified_);
for (uint256 i = 0; i < dexKeys_.length; ) {
bool swap0To1_;
unchecked {
if (path_[i] == dexKeys_[i].token0 && path_[i + 1] == dexKeys_[i].token1) {
swap0To1_ = true;
} else if (path_[i] == dexKeys_[i].token1 && path_[i + 1] == dexKeys_[i].token0) {
swap0To1_ = false;
} else {
revert InvalidPathTokenOrder();
}
}
amountUnspecified_ = _swapIn(dexKeys_[i], swap0To1_, amountUnspecified_);
if (amountUnspecified_ < amountLimits_[i]) {
revert AmountLimitNotMet(amountUnspecified_, amountLimits_[i]);
}
unchecked { ++i; }
}
if (transferParams_.extraData.length == 0) {
_transferTokens(
path_[0],
uint256(amountSpecified_),
path_[dexKeys_.length],
amountUnspecified_,
transferParams_.to,
transferParams_.isCallback,
transferParams_.callbackData
);
} else if (bytes32(transferParams_.extraData) == ESTIMATE_SWAP) {
revert EstimateSwap(amountUnspecified_);
} else {
_callExtraDataSlot(
abi.encode(
SWAP_HOP,
abi.encode(path_, dexKeys_, amountSpecified_, amountUnspecified_, transferParams_.extraData)
)
);
}
} else {
// Swap Out (Exact output amount received by the user)
amountUnspecified_ = uint256(-amountSpecified_);
for (uint256 i = dexKeys_.length; i > 0; ) {
bool swap0To1_;
unchecked {
if (path_[i - 1] == dexKeys_[i - 1].token0 && path_[i] == dexKeys_[i - 1].token1) {
swap0To1_ = true;
} else if (path_[i - 1] == dexKeys_[i - 1].token1 && path_[i] == dexKeys_[i - 1].token0) {
swap0To1_ = false;
} else {
revert InvalidPathTokenOrder();
}
}
amountUnspecified_ = _swapOut(dexKeys_[i - 1], swap0To1_, amountUnspecified_);
if (amountUnspecified_ > amountLimits_[i - 1]) {
revert AmountLimitExceeded(amountUnspecified_, amountLimits_[i - 1]);
}
unchecked { --i; }
}
if (transferParams_.extraData.length == 0) {
_transferTokens(
path_[0],
amountUnspecified_,
path_[dexKeys_.length],
uint256(-amountSpecified_),
transferParams_.to,
transferParams_.isCallback,
transferParams_.callbackData
);
} else if (bytes32(transferParams_.extraData) == ESTIMATE_SWAP) {
revert EstimateSwap(amountUnspecified_);
} else {
_callExtraDataSlot(
abi.encode(
SWAP_HOP,
abi.encode(path_, dexKeys_, amountSpecified_, amountUnspecified_, transferParams_.extraData)
)
);
}
}
}
function readFromStorage(bytes32 slot_) external view returns (uint256 result_) {
assembly {
result_ := sload(slot_)
}
}
fallback(bytes calldata data_) external payable _reentrancyLock returns (bytes memory) {
if (_isAuth[msg.sender] != 1 && _getGovernanceAddr() != msg.sender) {
revert UnauthorizedCaller(msg.sender);
}
(address target_, bytes memory spellData_) = abi.decode(data_, (address, bytes));
return _spell(target_, spellData_);
}
receive() external payable {}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.21 <=0.8.29;
interface IProxy {
function setAdmin(address newAdmin_) external;
function setDummyImplementation(address newDummyImplementation_) external;
function addImplementation(address implementation_, bytes4[] calldata sigs_) external;
function removeImplementation(address implementation_) external;
function getAdmin() external view returns (address);
function getDummyImplementation() external view returns (address);
function getImplementationSigs(address impl_) external view returns (bytes4[] memory);
function getSigsImplementation(bytes4 sig_) external view returns (address);
function readFromStorage(bytes32 slot_) external view returns (uint256 result_);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.8.21 <=0.8.29;
/// @notice implements calculation of address for contracts deployed through CREATE.
/// Accepts contract deployed from which address & nonce
library AddressCalcs {
/// @notice Computes the address of a contract based
/// @param deployedFrom_ Address from which the contract was deployed
/// @param nonce_ Nonce at which the contract was deployed
/// @return contract_ Address of deployed contract
function addressCalc(address deployedFrom_, uint nonce_) internal pure returns (address contract_) {
// @dev based on https://ethereum.stackexchange.com/a/61413
// nonce of smart contract always starts with 1. so, with nonce 0 there won't be any deployment
// hence, nonce of vault deployment starts with 1.
bytes memory data;
if (nonce_ == 0x00) {
return address(0);
} else if (nonce_ <= 0x7f) {
data = abi.encodePacked(bytes1(0xd6), bytes1(0x94), deployedFrom_, uint8(nonce_));
} else if (nonce_ <= 0xff) {
data = abi.encodePacked(bytes1(0xd7), bytes1(0x94), deployedFrom_, bytes1(0x81), uint8(nonce_));
} else if (nonce_ <= 0xffff) {
data = abi.encodePacked(bytes1(0xd8), bytes1(0x94), deployedFrom_, bytes1(0x82), uint16(nonce_));
} else if (nonce_ <= 0xffffff) {
data = abi.encodePacked(bytes1(0xd9), bytes1(0x94), deployedFrom_, bytes1(0x83), uint24(nonce_));
} else {
data = abi.encodePacked(bytes1(0xda), bytes1(0x94), deployedFrom_, bytes1(0x84), uint32(nonce_));
}
return address(uint160(uint256(keccak256(data))));
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.8.21 <=0.8.29;
/// @title library that represents a number in BigNumber(coefficient and exponent) format to store in smaller bits.
/// @notice the number is divided into two parts: a coefficient and an exponent. This comes at a cost of losing some precision
/// at the end of the number because the exponent simply fills it with zeroes. This precision is oftentimes negligible and can
/// result in significant gas cost reduction due to storage space reduction.
/// Also note, a valid big number is as follows: if the exponent is > 0, then coefficient last bits should be occupied to have max precision.
/// @dev roundUp is more like a increase 1, which happens everytime for the same number.
/// roundDown simply sets trailing digits after coefficientSize to zero (floor), only once for the same number.
library BigMathMinified {
/// @dev constants to use for `roundUp` input param to increase readability
bool internal constant ROUND_DOWN = false;
bool internal constant ROUND_UP = true;
/// @dev converts `normal` number to BigNumber with `exponent` and `coefficient` (or precision).
/// e.g.:
/// 5035703444687813576399599 (normal) = (coefficient[32bits], exponent[8bits])[40bits]
/// 5035703444687813576399599 (decimal) => 10000101010010110100000011111011110010100110100000000011100101001101001101011101111 (binary)
/// => 10000101010010110100000011111011000000000000000000000000000000000000000000000000000
/// ^-------------------- 51(exponent) -------------- ^
/// coefficient = 1000,0101,0100,1011,0100,0000,1111,1011 (2236301563)
/// exponent = 0011,0011 (51)
/// bigNumber = 1000,0101,0100,1011,0100,0000,1111,1011,0011,0011 (572493200179)
///
/// @param normal number which needs to be converted into Big Number
/// @param coefficientSize at max how many bits of precision there should be (64 = uint64 (64 bits precision))
/// @param exponentSize at max how many bits of exponent there should be (8 = uint8 (8 bits exponent))
/// @param roundUp signals if result should be rounded down or up
/// @return bigNumber converted bigNumber (coefficient << exponent)
function toBigNumber(
uint256 normal,
uint256 coefficientSize,
uint256 exponentSize,
bool roundUp
) internal pure returns (uint256 bigNumber) {
assembly {
let lastBit_
let number_ := normal
if gt(number_, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) {
number_ := shr(0x80, number_)
lastBit_ := 0x80
}
if gt(number_, 0xFFFFFFFFFFFFFFFF) {
number_ := shr(0x40, number_)
lastBit_ := add(lastBit_, 0x40)
}
if gt(number_, 0xFFFFFFFF) {
number_ := shr(0x20, number_)
lastBit_ := add(lastBit_, 0x20)
}
if gt(number_, 0xFFFF) {
number_ := shr(0x10, number_)
lastBit_ := add(lastBit_, 0x10)
}
if gt(number_, 0xFF) {
number_ := shr(0x8, number_)
lastBit_ := add(lastBit_, 0x8)
}
if gt(number_, 0xF) {
number_ := shr(0x4, number_)
lastBit_ := add(lastBit_, 0x4)
}
if gt(number_, 0x3) {
number_ := shr(0x2, number_)
lastBit_ := add(lastBit_, 0x2)
}
if gt(number_, 0x1) {
lastBit_ := add(lastBit_, 1)
}
if gt(number_, 0) {
lastBit_ := add(lastBit_, 1)
}
if lt(lastBit_, coefficientSize) {
// for throw exception
lastBit_ := coefficientSize
}
let exponent := sub(lastBit_, coefficientSize)
let coefficient := shr(exponent, normal)
if and(roundUp, gt(exponent, 0)) {
// rounding up is only needed if exponent is > 0, as otherwise the coefficient fully holds the original number
coefficient := add(coefficient, 1)
if eq(shl(coefficientSize, 1), coefficient) {
// case were coefficient was e.g. 111, with adding 1 it became 1000 (in binary) and coefficientSize 3 bits
// final coefficient would exceed it's size. -> reduce coefficent to 100 and increase exponent by 1.
coefficient := shl(sub(coefficientSize, 1), 1)
exponent := add(exponent, 1)
}
}
if iszero(lt(exponent, shl(exponentSize, 1))) {
// if exponent is >= exponentSize, the normal number is too big to fit within
// BigNumber with too small sizes for coefficient and exponent
revert(0, 0)
}
bigNumber := shl(exponentSize, coefficient)
bigNumber := add(bigNumber, exponent)
}
}
/// @dev get `normal` number from `bigNumber`, `exponentSize` and `exponentMask`
function fromBigNumber(
uint256 bigNumber,
uint256 exponentSize,
uint256 exponentMask
) internal pure returns (uint256 normal) {
assembly {
let coefficient := shr(exponentSize, bigNumber)
let exponent := and(bigNumber, exponentMask)
normal := shl(exponent, coefficient)
}
}
/// @dev gets the most significant bit `lastBit` of a `normal` number (length of given number of binary format).
/// e.g.
/// 5035703444687813576399584 = 10000101010010110100000011111011110010100110100000000011100101001101001101011100000
/// lastBit = ^--------------------------------- 83 ----------------------------------------^
function mostSignificantBit(uint256 normal) internal pure returns (uint lastBit) {
assembly {
let number_ := normal
if gt(normal, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) {
number_ := shr(0x80, number_)
lastBit := 0x80
}
if gt(number_, 0xFFFFFFFFFFFFFFFF) {
number_ := shr(0x40, number_)
lastBit := add(lastBit, 0x40)
}
if gt(number_, 0xFFFFFFFF) {
number_ := shr(0x20, number_)
lastBit := add(lastBit, 0x20)
}
if gt(number_, 0xFFFF) {
number_ := shr(0x10, number_)
lastBit := add(lastBit, 0x10)
}
if gt(number_, 0xFF) {
number_ := shr(0x8, number_)
lastBit := add(lastBit, 0x8)
}
if gt(number_, 0xF) {
number_ := shr(0x4, number_)
lastBit := add(lastBit, 0x4)
}
if gt(number_, 0x3) {
number_ := shr(0x2, number_)
lastBit := add(lastBit, 0x2)
}
if gt(number_, 0x1) {
lastBit := add(lastBit, 1)
}
if gt(number_, 0) {
lastBit := add(lastBit, 1)
}
}
}
/// @dev gets the least significant bit `firstBit` of a `normal` number (position of rightmost 1 in binary format).
/// e.g.
/// 5035703444687813576399584 = 10000101010010110100000011111011110010100110100000000011100101001101001101011100000
/// firstBit = ^-6--^
function leastSignificantBit(uint256 normal) internal pure returns (uint firstBit) {
assembly {
// If number is 0, revert as there is no least significant bit
if iszero(normal) {
revert(0, 0)
}
// Find first set bit using binary search
let number_ := normal
firstBit := 0
// Check if lower 128 bits are all zero
if iszero(and(number_, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)) {
number_ := shr(0x80, number_)
firstBit := 0x80
}
// Check if lower 64 bits are all zero
if iszero(and(number_, 0xFFFFFFFFFFFFFFFF)) {
number_ := shr(0x40, number_)
firstBit := add(firstBit, 0x40)
}
// Check if lower 32 bits are all zero
if iszero(and(number_, 0xFFFFFFFF)) {
number_ := shr(0x20, number_)
firstBit := add(firstBit, 0x20)
}
// Check if lower 16 bits are all zero
if iszero(and(number_, 0xFFFF)) {
number_ := shr(0x10, number_)
firstBit := add(firstBit, 0x10)
}
// Check if lower 8 bits are all zero
if iszero(and(number_, 0xFF)) {
number_ := shr(0x8, number_)
firstBit := add(firstBit, 0x8)
}
// Check if lower 4 bits are all zero
if iszero(and(number_, 0xF)) {
number_ := shr(0x4, number_)
firstBit := add(firstBit, 0x4)
}
// Check if lower 2 bits are all zero
if iszero(and(number_, 0x3)) {
number_ := shr(0x2, number_)
firstBit := add(firstBit, 0x2)
}
// Check if lowest bit is zero
if iszero(and(number_, 0x1)) {
firstBit := add(firstBit, 1)
}
// Add 1 to match the 1-based position counting
firstBit := add(firstBit, 1)
}
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.29;
/// @notice library that helps in reading / working with storage slot data of Fluid Dex Lite.
library DexLiteSlotsLink {
/// @dev storage slot for is auth mapping
uint256 internal constant DEX_LITE_IS_AUTH_SLOT = 0;
/// @dev storage slot for dexes list
uint256 internal constant DEX_LITE_DEXES_LIST_SLOT = 1;
/// @dev storage slot for is dex variables
uint256 internal constant DEX_LITE_DEX_VARIABLES_SLOT = 2;
/// @dev storage slot for center price shift
uint256 internal constant DEX_LITE_CENTER_PRICE_SHIFT_SLOT = 3;
/// @dev storage slot for range shift
uint256 internal constant DEX_LITE_RANGE_SHIFT_SLOT = 4;
/// @dev storage slot for threshold shift
uint256 internal constant DEX_LITE_THRESHOLD_SHIFT_SLOT = 5;
// --------------------------------
// @dev stacked uint256 storage slots bits position data for each:
// DexVariables
uint256 internal constant BITS_DEX_LITE_DEX_VARIABLES_FEE = 0;
uint256 internal constant BITS_DEX_LITE_DEX_VARIABLES_REVENUE_CUT = 13;
uint256 internal constant BITS_DEX_LITE_DEX_VARIABLES_REBALANCING_STATUS = 20;
uint256 internal constant BITS_DEX_LITE_DEX_VARIABLES_CENTER_PRICE_SHIFT_ACTIVE = 22;
uint256 internal constant BITS_DEX_LITE_DEX_VARIABLES_CENTER_PRICE = 23;
uint256 internal constant BITS_DEX_LITE_DEX_VARIABLES_CENTER_PRICE_CONTRACT_ADDRESS = 63;
uint256 internal constant BITS_DEX_LITE_DEX_VARIABLES_RANGE_PERCENT_SHIFT_ACTIVE = 82;
uint256 internal constant BITS_DEX_LITE_DEX_VARIABLES_UPPER_PERCENT = 83;
uint256 internal constant BITS_DEX_LITE_DEX_VARIABLES_LOWER_PERCENT = 97;
uint256 internal constant BITS_DEX_LITE_DEX_VARIABLES_THRESHOLD_PERCENT_SHIFT_ACTIVE = 111;
uint256 internal constant BITS_DEX_LITE_DEX_VARIABLES_UPPER_SHIFT_THRESHOLD_PERCENT = 112;
uint256 internal constant BITS_DEX_LITE_DEX_VARIABLES_LOWER_SHIFT_THRESHOLD_PERCENT = 119;
uint256 internal constant BITS_DEX_LITE_DEX_VARIABLES_TOKEN_0_DECIMALS = 126;
uint256 internal constant BITS_DEX_LITE_DEX_VARIABLES_TOKEN_1_DECIMALS = 131;
uint256 internal constant BITS_DEX_LITE_DEX_VARIABLES_TOKEN_0_TOTAL_SUPPLY_ADJUSTED = 136;
uint256 internal constant BITS_DEX_LITE_DEX_VARIABLES_TOKEN_1_TOTAL_SUPPLY_ADJUSTED = 196;
// CenterPriceShift
uint256 internal constant BITS_DEX_LITE_CENTER_PRICE_SHIFT_LAST_INTERACTION_TIMESTAMP = 0;
uint256 internal constant BITS_DEX_LITE_CENTER_PRICE_SHIFT_SHIFTING_TIME = 33;
uint256 internal constant BITS_DEX_LITE_CENTER_PRICE_SHIFT_MAX_CENTER_PRICE = 57;
uint256 internal constant BITS_DEX_LITE_CENTER_PRICE_SHIFT_MIN_CENTER_PRICE = 85;
uint256 internal constant BITS_DEX_LITE_CENTER_PRICE_SHIFT_PERCENT = 113;
uint256 internal constant BITS_DEX_LITE_CENTER_PRICE_SHIFT_TIME_TO_SHIFT = 133;
uint256 internal constant BITS_DEX_LITE_CENTER_PRICE_SHIFT_TIMESTAMP = 153;
// RangeShift
uint256 internal constant BITS_DEX_LITE_RANGE_SHIFT_OLD_UPPER_RANGE_PERCENT = 0;
uint256 internal constant BITS_DEX_LITE_RANGE_SHIFT_OLD_LOWER_RANGE_PERCENT = 14;
uint256 internal constant BITS_DEX_LITE_RANGE_SHIFT_TIME_TO_SHIFT = 28;
uint256 internal constant BITS_DEX_LITE_RANGE_SHIFT_TIMESTAMP = 48;
// ThresholdShift
uint256 internal constant BITS_DEX_LITE_THRESHOLD_SHIFT_OLD_UPPER_THRESHOLD_PERCENT = 0;
uint256 internal constant BITS_DEX_LITE_THRESHOLD_SHIFT_OLD_LOWER_THRESHOLD_PERCENT = 7;
uint256 internal constant BITS_DEX_LITE_THRESHOLD_SHIFT_TIME_TO_SHIFT = 14;
uint256 internal constant BITS_DEX_LITE_THRESHOLD_SHIFT_TIMESTAMP = 34;
// --------------------------------
// @dev stacked uint256 swapData for LogSwap event
uint256 internal constant BITS_DEX_LITE_SWAP_DATA_DEX_ID = 0;
uint256 internal constant BITS_DEX_LITE_SWAP_DATA_SWAP_0_TO_1 = 64;
uint256 internal constant BITS_DEX_LITE_SWAP_DATA_AMOUNT_IN = 65;
uint256 internal constant BITS_DEX_LITE_SWAP_DATA_AMOUNT_OUT = 125;
/// @notice Calculating the slot ID for Dex contract for single mapping at `slot_` for `key_`
function calculateMappingStorageSlot(uint256 slot_, bytes32 key_) internal pure returns (bytes32) {
return keccak256(abi.encode(key_, slot_));
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.8.21 <=0.8.29;
library LibsErrorTypes {
/***********************************|
| LiquidityCalcs |
|__________________________________*/
/// @notice thrown when supply or borrow exchange price is zero at calc token data (token not configured yet)
uint256 internal constant LiquidityCalcs__ExchangePriceZero = 70001;
/// @notice thrown when rate data is set to a version that is not implemented
uint256 internal constant LiquidityCalcs__UnsupportedRateVersion = 70002;
/// @notice thrown when the calculated borrow rate turns negative. This should never happen.
uint256 internal constant LiquidityCalcs__BorrowRateNegative = 70003;
/***********************************|
| SafeTransfer |
|__________________________________*/
/// @notice thrown when safe transfer from for an ERC20 fails
uint256 internal constant SafeTransfer__TransferFromFailed = 71001;
/// @notice thrown when safe transfer for an ERC20 fails
uint256 internal constant SafeTransfer__TransferFailed = 71002;
/***********************************|
| SafeApprove |
|__________________________________*/
/// @notice thrown when safe approve from for an ERC20 fails
uint256 internal constant SafeApprove__ApproveFailed = 81001;
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.29;
library ReentrancyLock {
// bytes32(uint256(keccak256("FLUID_REENTRANCY_LOCK")) - 1)
bytes32 constant REENTRANCY_LOCK_SLOT = 0xb9cde754d19acfff2b3ccabc66f256d3563a0bc5805da4205f01a9bda38a2df7;
function lock() internal {
assembly {
if tload(REENTRANCY_LOCK_SLOT) { revert(0, 0) }
tstore(REENTRANCY_LOCK_SLOT, 1)
}
}
function unlock() internal {
assembly { tstore(REENTRANCY_LOCK_SLOT, 0) }
}
}// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity >=0.8.21 <=0.8.29;
import { LibsErrorTypes as ErrorTypes } from "./errorTypes.sol";
/// @notice provides minimalistic methods for safe transfers, e.g. ERC20 safeTransferFrom
library SafeTransfer {
uint256 internal constant MAX_NATIVE_TRANSFER_GAS = 20000; // pass max. 20k gas for native transfers
error FluidSafeTransferError(uint256 errorId_);
/// @dev Transfer `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.
/// Minimally modified from Solmate SafeTransferLib (address as input param for token, Custom Error):
/// https://github.com/transmissions11/solmate/blob/50e15bb566f98b7174da9b0066126a4c3e75e0fd/src/utils/SafeTransferLib.sol#L31-L63
function safeTransferFrom(address token_, address from_, address to_, uint256 amount_) internal {
bool success_;
/// @solidity memory-safe-assembly
assembly {
// Get a pointer to some free memory.
let freeMemoryPointer := mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.
mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), and(from_, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "from_" argument.
mstore(add(freeMemoryPointer, 36), and(to_, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to_" argument.
mstore(add(freeMemoryPointer, 68), amount_) // Append the "amount_" argument. Masking not required as it's a full 32 byte type.
success_ := and(
// Set success to whether the call reverted, if not we check it either
// returned exactly 1 (can't just be non-zero data), or had no return data.
or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
// We use 100 because the length of our calldata totals up like so: 4 + 32 * 3.
// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
// Counterintuitively, this call must be positioned second to the or() call in the
// surrounding and() call or else returndatasize() will be zero during the computation.
call(gas(), token_, 0, freeMemoryPointer, 100, 0, 32)
)
}
if (!success_) {
revert FluidSafeTransferError(ErrorTypes.SafeTransfer__TransferFromFailed);
}
}
/// @dev Transfer `amount_` of `token_` to `to_`.
/// If `token_` returns no value, non-reverting calls are assumed to be successful.
/// Minimally modified from Solmate SafeTransferLib (address as input param for token, Custom Error):
/// https://github.com/transmissions11/solmate/blob/50e15bb566f98b7174da9b0066126a4c3e75e0fd/src/utils/SafeTransferLib.sol#L65-L95
function safeTransfer(address token_, address to_, uint256 amount_) internal {
bool success_;
/// @solidity memory-safe-assembly
assembly {
// Get a pointer to some free memory.
let freeMemoryPointer := mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.
mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), and(to_, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to_" argument.
mstore(add(freeMemoryPointer, 36), amount_) // Append the "amount_" argument. Masking not required as it's a full 32 byte type.
success_ := and(
// Set success to whether the call reverted, if not we check it either
// returned exactly 1 (can't just be non-zero data), or had no return data.
or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
// We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
// Counterintuitively, this call must be positioned second to the or() call in the
// surrounding and() call or else returndatasize() will be zero during the computation.
call(gas(), token_, 0, freeMemoryPointer, 68, 0, 32)
)
}
if (!success_) {
revert FluidSafeTransferError(ErrorTypes.SafeTransfer__TransferFailed);
}
}
/// @dev Transfer `amount_` of ` native token to `to_`.
/// Minimally modified from Solmate SafeTransferLib (Custom Error):
/// https://github.com/transmissions11/solmate/blob/50e15bb566f98b7174da9b0066126a4c3e75e0fd/src/utils/SafeTransferLib.sol#L15-L25
function safeTransferNative(address to_, uint256 amount_) internal {
bool success_;
/// @solidity memory-safe-assembly
assembly {
// Transfer the ETH and store if it succeeded or not. Pass limited gas
success_ := call(MAX_NATIVE_TRANSFER_GAS, to_, amount_, 0, 0, 0, 0)
}
if (!success_) {
revert FluidSafeTransferError(ErrorTypes.SafeTransfer__TransferFailed);
}
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.8.21 <=0.8.29;
abstract contract Structs {
struct AddressBool {
address addr;
bool value;
}
struct AddressUint256 {
address addr;
uint256 value;
}
/// @notice struct to set borrow rate data for version 1
struct RateDataV1Params {
///
/// @param token for rate data
address token;
///
/// @param kink in borrow rate. in 1e2: 100% = 10_000; 1% = 100
/// utilization below kink usually means slow increase in rate, once utilization is above kink borrow rate increases fast
uint256 kink;
///
/// @param rateAtUtilizationZero desired borrow rate when utilization is zero. in 1e2: 100% = 10_000; 1% = 100
/// i.e. constant minimum borrow rate
/// e.g. at utilization = 0.01% rate could still be at least 4% (rateAtUtilizationZero would be 400 then)
uint256 rateAtUtilizationZero;
///
/// @param rateAtUtilizationKink borrow rate when utilization is at kink. in 1e2: 100% = 10_000; 1% = 100
/// e.g. when rate should be 7% at kink then rateAtUtilizationKink would be 700
uint256 rateAtUtilizationKink;
///
/// @param rateAtUtilizationMax borrow rate when utilization is maximum at 100%. in 1e2: 100% = 10_000; 1% = 100
/// e.g. when rate should be 125% at 100% then rateAtUtilizationMax would be 12_500
uint256 rateAtUtilizationMax;
}
/// @notice struct to set borrow rate data for version 2
struct RateDataV2Params {
///
/// @param token for rate data
address token;
///
/// @param kink1 first kink in borrow rate. in 1e2: 100% = 10_000; 1% = 100
/// utilization below kink 1 usually means slow increase in rate, once utilization is above kink 1 borrow rate increases faster
uint256 kink1;
///
/// @param kink2 second kink in borrow rate. in 1e2: 100% = 10_000; 1% = 100
/// utilization below kink 2 usually means slow / medium increase in rate, once utilization is above kink 2 borrow rate increases fast
uint256 kink2;
///
/// @param rateAtUtilizationZero desired borrow rate when utilization is zero. in 1e2: 100% = 10_000; 1% = 100
/// i.e. constant minimum borrow rate
/// e.g. at utilization = 0.01% rate could still be at least 4% (rateAtUtilizationZero would be 400 then)
uint256 rateAtUtilizationZero;
///
/// @param rateAtUtilizationKink1 desired borrow rate when utilization is at first kink. in 1e2: 100% = 10_000; 1% = 100
/// e.g. when rate should be 7% at first kink then rateAtUtilizationKink would be 700
uint256 rateAtUtilizationKink1;
///
/// @param rateAtUtilizationKink2 desired borrow rate when utilization is at second kink. in 1e2: 100% = 10_000; 1% = 100
/// e.g. when rate should be 7% at second kink then rateAtUtilizationKink would be 1_200
uint256 rateAtUtilizationKink2;
///
/// @param rateAtUtilizationMax desired borrow rate when utilization is maximum at 100%. in 1e2: 100% = 10_000; 1% = 100
/// e.g. when rate should be 125% at 100% then rateAtUtilizationMax would be 12_500
uint256 rateAtUtilizationMax;
}
/// @notice struct to set token config
struct TokenConfig {
///
/// @param token address
address token;
///
/// @param fee charges on borrower's interest. in 1e2: 100% = 10_000; 1% = 100
uint256 fee;
///
/// @param threshold on when to update the storage slot. in 1e2: 100% = 10_000; 1% = 100
uint256 threshold;
///
/// @param maxUtilization maximum allowed utilization. in 1e2: 100% = 10_000; 1% = 100
/// set to 100% to disable and have default limit of 100% (avoiding SLOAD).
uint256 maxUtilization;
}
/// @notice struct to set user supply & withdrawal config
struct UserSupplyConfig {
///
/// @param user address
address user;
///
/// @param token address
address token;
///
/// @param mode: 0 = without interest. 1 = with interest
uint8 mode;
///
/// @param expandPercent withdrawal limit expand percent. in 1e2: 100% = 10_000; 1% = 100
/// Also used to calculate rate at which withdrawal limit should decrease (instant).
uint256 expandPercent;
///
/// @param expandDuration withdrawal limit expand duration in seconds.
/// used to calculate rate together with expandPercent
uint256 expandDuration;
///
/// @param baseWithdrawalLimit base limit, below this, user can withdraw the entire amount.
/// amount in raw (to be multiplied with exchange price) or normal depends on configured mode in user config for the token:
/// with interest -> raw, without interest -> normal
uint256 baseWithdrawalLimit;
}
/// @notice struct to set user borrow & payback config
struct UserBorrowConfig {
///
/// @param user address
address user;
///
/// @param token address
address token;
///
/// @param mode: 0 = without interest. 1 = with interest
uint8 mode;
///
/// @param expandPercent debt limit expand percent. in 1e2: 100% = 10_000; 1% = 100
/// Also used to calculate rate at which debt limit should decrease (instant).
uint256 expandPercent;
///
/// @param expandDuration debt limit expand duration in seconds.
/// used to calculate rate together with expandPercent
uint256 expandDuration;
///
/// @param baseDebtCeiling base borrow limit. until here, borrow limit remains as baseDebtCeiling
/// (user can borrow until this point at once without stepped expansion). Above this, automated limit comes in place.
/// amount in raw (to be multiplied with exchange price) or normal depends on configured mode in user config for the token:
/// with interest -> raw, without interest -> normal
uint256 baseDebtCeiling;
///
/// @param maxDebtCeiling max borrow ceiling, maximum amount the user can borrow.
/// amount in raw (to be multiplied with exchange price) or normal depends on configured mode in user config for the token:
/// with interest -> raw, without interest -> normal
uint256 maxDebtCeiling;
}
}//SPDX-License-Identifier: MIT
pragma solidity >=0.8.21 <=0.8.29;
import { IProxy } from "../../infiniteProxy/interfaces/iProxy.sol";
import { Structs as AdminModuleStructs } from "../adminModule/structs.sol";
interface IFluidLiquidityAdmin {
/// @notice adds/removes auths. Auths generally could be contracts which can have restricted actions defined on contract.
/// auths can be helpful in reducing governance overhead where it's not needed.
/// @param authsStatus_ array of structs setting allowed status for an address.
/// status true => add auth, false => remove auth
function updateAuths(AdminModuleStructs.AddressBool[] calldata authsStatus_) external;
/// @notice adds/removes guardians. Only callable by Governance.
/// @param guardiansStatus_ array of structs setting allowed status for an address.
/// status true => add guardian, false => remove guardian
function updateGuardians(AdminModuleStructs.AddressBool[] calldata guardiansStatus_) external;
/// @notice changes the revenue collector address (contract that is sent revenue). Only callable by Governance.
/// @param revenueCollector_ new revenue collector address
function updateRevenueCollector(address revenueCollector_) external;
/// @notice changes current status, e.g. for pausing or unpausing all user operations. Only callable by Auths.
/// @param newStatus_ new status
/// status = 2 -> pause, status = 1 -> resume.
function changeStatus(uint256 newStatus_) external;
/// @notice update tokens rate data version 1. Only callable by Auths.
/// @param tokensRateData_ array of RateDataV1Params with rate data to set for each token
function updateRateDataV1s(AdminModuleStructs.RateDataV1Params[] calldata tokensRateData_) external;
/// @notice update tokens rate data version 2. Only callable by Auths.
/// @param tokensRateData_ array of RateDataV2Params with rate data to set for each token
function updateRateDataV2s(AdminModuleStructs.RateDataV2Params[] calldata tokensRateData_) external;
/// @notice updates token configs: fee charge on borrowers interest & storage update utilization threshold.
/// Only callable by Auths.
/// @param tokenConfigs_ contains token address, fee & utilization threshold
function updateTokenConfigs(AdminModuleStructs.TokenConfig[] calldata tokenConfigs_) external;
/// @notice updates user classes: 0 is for new protocols, 1 is for established protocols.
/// Only callable by Auths.
/// @param userClasses_ struct array of uint256 value to assign for each user address
function updateUserClasses(AdminModuleStructs.AddressUint256[] calldata userClasses_) external;
/// @notice sets user supply configs per token basis. Eg: with interest or interest-free and automated limits.
/// Only callable by Auths.
/// @param userSupplyConfigs_ struct array containing user supply config, see `UserSupplyConfig` struct for more info
function updateUserSupplyConfigs(AdminModuleStructs.UserSupplyConfig[] memory userSupplyConfigs_) external;
/// @notice sets a new withdrawal limit as the current limit for a certain user
/// @param user_ user address for which to update the withdrawal limit
/// @param token_ token address for which to update the withdrawal limit
/// @param newLimit_ new limit until which user supply can decrease to.
/// Important: input in raw. Must account for exchange price in input param calculation.
/// Note any limit that is < max expansion or > current user supply will set max expansion limit or
/// current user supply as limit respectively.
/// - set 0 to make maximum possible withdrawable: instant full expansion, and if that goes
/// below base limit then fully down to 0.
/// - set type(uint256).max to make current withdrawable 0 (sets current user supply as limit).
function updateUserWithdrawalLimit(address user_, address token_, uint256 newLimit_) external;
/// @notice setting user borrow configs per token basis. Eg: with interest or interest-free and automated limits.
/// Only callable by Auths.
/// @param userBorrowConfigs_ struct array containing user borrow config, see `UserBorrowConfig` struct for more info
function updateUserBorrowConfigs(AdminModuleStructs.UserBorrowConfig[] memory userBorrowConfigs_) external;
/// @notice pause operations for a particular user in class 0 (class 1 users can't be paused by guardians).
/// Only callable by Guardians.
/// @param user_ address of user to pause operations for
/// @param supplyTokens_ token addresses to pause withdrawals for
/// @param borrowTokens_ token addresses to pause borrowings for
function pauseUser(address user_, address[] calldata supplyTokens_, address[] calldata borrowTokens_) external;
/// @notice unpause operations for a particular user in class 0 (class 1 users can't be paused by guardians).
/// Only callable by Guardians.
/// @param user_ address of user to unpause operations for
/// @param supplyTokens_ token addresses to unpause withdrawals for
/// @param borrowTokens_ token addresses to unpause borrowings for
function unpauseUser(address user_, address[] calldata supplyTokens_, address[] calldata borrowTokens_) external;
/// @notice collects revenue for tokens to configured revenueCollector address.
/// @param tokens_ array of tokens to collect revenue for
/// @dev Note that this can revert if token balance is < revenueAmount (utilization > 100%)
function collectRevenue(address[] calldata tokens_) external;
/// @notice gets the current updated exchange prices for n tokens and updates all prices, rates related data in storage.
/// @param tokens_ tokens to update exchange prices for
/// @return supplyExchangePrices_ new supply rates of overall system for each token
/// @return borrowExchangePrices_ new borrow rates of overall system for each token
function updateExchangePrices(
address[] calldata tokens_
) external returns (uint256[] memory supplyExchangePrices_, uint256[] memory borrowExchangePrices_);
}
interface IFluidLiquidityLogic is IFluidLiquidityAdmin {
/// @notice Single function which handles supply, withdraw, borrow & payback
/// @param token_ address of token (0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE for native)
/// @param supplyAmount_ if +ve then supply, if -ve then withdraw, if 0 then nothing
/// @param borrowAmount_ if +ve then borrow, if -ve then payback, if 0 then nothing
/// @param withdrawTo_ if withdrawal then to which address
/// @param borrowTo_ if borrow then to which address
/// @param callbackData_ callback data passed to `liquidityCallback` method of protocol
/// @return memVar3_ updated supplyExchangePrice
/// @return memVar4_ updated borrowExchangePrice
/// @dev to trigger skipping in / out transfers (gas optimization):
/// - ` callbackData_` MUST be encoded so that "from" address is the last 20 bytes in the last 32 bytes slot,
/// also for native token operations where liquidityCallback is not triggered!
/// from address must come at last position if there is more data. I.e. encode like:
/// abi.encode(otherVar1, otherVar2, FROM_ADDRESS). Note dynamic types used with abi.encode come at the end
/// so if dynamic types are needed, you must use abi.encodePacked to ensure the from address is at the end.
/// - this "from" address must match withdrawTo_ or borrowTo_ and must be == `msg.sender`
/// - `callbackData_` must in addition to the from address as described above include bytes32 SKIP_TRANSFERS
/// in the slot before (bytes 32 to 63)
/// - `msg.value` must be 0.
/// - Amounts must be either:
/// - supply(+) == borrow(+), withdraw(-) == payback(-).
/// - Liquidity must be on the winning side (deposit < borrow OR payback < withdraw).
function operate(
address token_,
int256 supplyAmount_,
int256 borrowAmount_,
address withdrawTo_,
address borrowTo_,
bytes calldata callbackData_
) external payable returns (uint256 memVar3_, uint256 memVar4_);
}
interface IFluidLiquidity is IProxy, IFluidLiquidityLogic {}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.29;
import "./helpers.sol";
abstract contract CoreInternals is Helpers {
function _swapIn(
DexKey calldata dexKey_,
bool swap0To1_,
uint256 amountIn_
) internal returns (uint256 amountOut_) {
bytes8 dexId_ = bytes8(keccak256(abi.encode(dexKey_)));
uint256 dexVariables_ = _dexVariables[dexId_];
if (dexVariables_ == 0) {
revert DexNotInitialized(dexId_);
}
uint256 token0AdjustedSupply_ = (dexVariables_ >> DSL.BITS_DEX_LITE_DEX_VARIABLES_TOKEN_0_TOTAL_SUPPLY_ADJUSTED) & X60;
uint256 token1AdjustedSupply_ = (dexVariables_ >> DSL.BITS_DEX_LITE_DEX_VARIABLES_TOKEN_1_TOTAL_SUPPLY_ADJUSTED) & X60;
(uint256 centerPrice_, uint256 token0ImaginaryReserves_, uint256 token1ImaginaryReserves_) =
_getPricesAndReserves(dexKey_, dexVariables_, dexId_, token0AdjustedSupply_, token1AdjustedSupply_);
unchecked {
if (swap0To1_) {
uint256 token0Decimals_ = (dexVariables_ >> DSL.BITS_DEX_LITE_DEX_VARIABLES_TOKEN_0_DECIMALS) & X5;
if (token0Decimals_ > TOKENS_DECIMALS_PRECISION) {
amountIn_ /= _tenPow(token0Decimals_ - TOKENS_DECIMALS_PRECISION);
} else {
amountIn_ *= _tenPow(TOKENS_DECIMALS_PRECISION - token0Decimals_);
}
if (amountIn_ < FOUR_DECIMALS || amountIn_ > X60) {
revert InvalidSwapAmounts(amountIn_);
}
if (amountIn_ > (token0ImaginaryReserves_ / 2)) {
revert ExcessiveSwapAmount(amountIn_, token0ImaginaryReserves_);
}
uint256 fee_ = (amountIn_ * ((dexVariables_ >> DSL.BITS_DEX_LITE_DEX_VARIABLES_FEE) & X13)) / SIX_DECIMALS;
// amountOut = (amountIn * iReserveOut) / (iReserveIn + amountIn)
amountOut_ = ((amountIn_ - fee_) * token1ImaginaryReserves_) / (token0ImaginaryReserves_ + (amountIn_ - fee_));
token0AdjustedSupply_ += amountIn_ - ((fee_ * ((dexVariables_ >> DSL.BITS_DEX_LITE_DEX_VARIABLES_REVENUE_CUT) & X7)) / TWO_DECIMALS);
if (token1AdjustedSupply_ < amountOut_) {
revert TokenReservesTooLow(amountOut_, token1AdjustedSupply_);
}
token1AdjustedSupply_ -= amountOut_;
if (((token1AdjustedSupply_) < ((token0AdjustedSupply_ * centerPrice_) / (PRICE_PRECISION * MINIMUM_LIQUIDITY_SWAP)))) {
revert TokenReservesRatioTooHigh(token0AdjustedSupply_, token1AdjustedSupply_);
}
} else {
uint256 token1Decimals_ = (dexVariables_ >> DSL.BITS_DEX_LITE_DEX_VARIABLES_TOKEN_1_DECIMALS) & X5;
if (token1Decimals_ > TOKENS_DECIMALS_PRECISION) {
amountIn_ /= _tenPow(token1Decimals_ - TOKENS_DECIMALS_PRECISION);
} else {
amountIn_ *= _tenPow(TOKENS_DECIMALS_PRECISION - token1Decimals_);
}
if (amountIn_ < FOUR_DECIMALS || amountIn_ > X60) {
revert InvalidSwapAmounts(amountIn_);
}
if (amountIn_ > (token1ImaginaryReserves_ / 2)) {
revert ExcessiveSwapAmount(amountIn_, token1ImaginaryReserves_);
}
uint256 fee_ = (amountIn_ * ((dexVariables_ >> DSL.BITS_DEX_LITE_DEX_VARIABLES_FEE) & X13)) / SIX_DECIMALS;
// amountOut = (amountIn * iReserveOut) / (iReserveIn + amountIn)
amountOut_ = ((amountIn_ - fee_) * token0ImaginaryReserves_) / (token1ImaginaryReserves_ + (amountIn_ - fee_));
token1AdjustedSupply_ += amountIn_ - ((fee_ * ((dexVariables_ >> DSL.BITS_DEX_LITE_DEX_VARIABLES_REVENUE_CUT) & X7)) / TWO_DECIMALS);
if (token0AdjustedSupply_ < amountOut_) {
revert TokenReservesTooLow(amountOut_, token0AdjustedSupply_);
}
token0AdjustedSupply_ -= amountOut_;
if (((token0AdjustedSupply_) < ((token1AdjustedSupply_ * PRICE_PRECISION) / (centerPrice_ * MINIMUM_LIQUIDITY_SWAP)))) {
revert TokenReservesRatioTooHigh(token0AdjustedSupply_, token1AdjustedSupply_);
}
}
}
{
uint256 rebalancingStatus_ = (dexVariables_ >> DSL.BITS_DEX_LITE_DEX_VARIABLES_REBALANCING_STATUS) & X2;
if (rebalancingStatus_ > 0) {
// rebalancing is active
uint256 price_;
unchecked {
price_ = (swap0To1_ ? (token1ImaginaryReserves_ - amountOut_) * PRICE_PRECISION / (token0ImaginaryReserves_ + amountIn_) :
(token1ImaginaryReserves_ + amountIn_) * PRICE_PRECISION / (token0ImaginaryReserves_ - amountOut_));
}
rebalancingStatus_ = _getRebalancingStatus(dexVariables_, dexId_, rebalancingStatus_, price_, centerPrice_);
}
// NOTE: we are using dexVariables_ and not _dexVariables[dexId_] here to check if the center price shift is active
// _dexVariables[dexId_] might have become inactive in this transaction above, but still we are storing the timestamp for this transaction
// storing the timestamp is not important, but we are still doing it because we don't want to use _dexVariables[dexId_] here because of gas
if (rebalancingStatus_ > 1 || ((dexVariables_ >> DSL.BITS_DEX_LITE_DEX_VARIABLES_CENTER_PRICE_SHIFT_ACTIVE) & X1) == 1) {
_centerPriceShift[dexId_] = _centerPriceShift[dexId_] & ~(X33 << DSL.BITS_DEX_LITE_CENTER_PRICE_SHIFT_LAST_INTERACTION_TIMESTAMP) |
(block.timestamp << DSL.BITS_DEX_LITE_CENTER_PRICE_SHIFT_LAST_INTERACTION_TIMESTAMP);
}
}
if (token0AdjustedSupply_ > X60 || token1AdjustedSupply_ > X60) {
revert AdjustedSupplyOverflow(dexId_, token0AdjustedSupply_, token1AdjustedSupply_);
}
dexVariables_ = _dexVariables[dexId_] & ~(X120 << DSL.BITS_DEX_LITE_DEX_VARIABLES_TOKEN_0_TOTAL_SUPPLY_ADJUSTED) |
(token0AdjustedSupply_ << DSL.BITS_DEX_LITE_DEX_VARIABLES_TOKEN_0_TOTAL_SUPPLY_ADJUSTED) |
(token1AdjustedSupply_ << DSL.BITS_DEX_LITE_DEX_VARIABLES_TOKEN_1_TOTAL_SUPPLY_ADJUSTED);
_dexVariables[dexId_] = dexVariables_;
if (swap0To1_) {
emit LogSwap(
(uint256(uint64(dexId_)) << DSL.BITS_DEX_LITE_SWAP_DATA_DEX_ID) |
(uint256(1) << DSL.BITS_DEX_LITE_SWAP_DATA_SWAP_0_TO_1) | // swap 0 to 1 bit is 1
(amountIn_ << DSL.BITS_DEX_LITE_SWAP_DATA_AMOUNT_IN) |
(amountOut_ << DSL.BITS_DEX_LITE_SWAP_DATA_AMOUNT_OUT),
dexVariables_
);
uint256 token1Decimals_ = (dexVariables_ >> DSL.BITS_DEX_LITE_DEX_VARIABLES_TOKEN_1_DECIMALS) & X5;
if (token1Decimals_ > TOKENS_DECIMALS_PRECISION) {
amountOut_ *= _tenPow(token1Decimals_ - TOKENS_DECIMALS_PRECISION);
} else {
amountOut_ /= _tenPow(TOKENS_DECIMALS_PRECISION - token1Decimals_);
}
} else {
emit LogSwap(
(uint256(uint64(dexId_)) << DSL.BITS_DEX_LITE_SWAP_DATA_DEX_ID) |
// swap 0 to 1 bit is 0
(amountIn_ << DSL.BITS_DEX_LITE_SWAP_DATA_AMOUNT_IN) |
(amountOut_ << DSL.BITS_DEX_LITE_SWAP_DATA_AMOUNT_OUT),
dexVariables_
);
uint256 token0Decimals_ = (dexVariables_ >> DSL.BITS_DEX_LITE_DEX_VARIABLES_TOKEN_0_DECIMALS) & X5;
if (token0Decimals_ > TOKENS_DECIMALS_PRECISION) {
amountOut_ *= _tenPow(token0Decimals_ - TOKENS_DECIMALS_PRECISION);
} else {
amountOut_ /= _tenPow(TOKENS_DECIMALS_PRECISION - token0Decimals_);
}
}
}
function _swapOut(
DexKey calldata dexKey_,
bool swap0To1_,
uint256 amountOut_
) internal returns (uint256 amountIn_) {
bytes8 dexId_ = bytes8(keccak256(abi.encode(dexKey_)));
uint256 dexVariables_ = _dexVariables[dexId_];
if (dexVariables_ == 0) {
revert DexNotInitialized(dexId_);
}
uint256 token0AdjustedSupply_ = (dexVariables_ >> DSL.BITS_DEX_LITE_DEX_VARIABLES_TOKEN_0_TOTAL_SUPPLY_ADJUSTED) & X60;
uint256 token1AdjustedSupply_ = (dexVariables_ >> DSL.BITS_DEX_LITE_DEX_VARIABLES_TOKEN_1_TOTAL_SUPPLY_ADJUSTED) & X60;
(uint256 centerPrice_, uint256 token0ImaginaryReserves_, uint256 token1ImaginaryReserves_) =
_getPricesAndReserves(dexKey_, dexVariables_, dexId_, token0AdjustedSupply_, token1AdjustedSupply_);
unchecked {
if (swap0To1_) {
uint256 token1Decimals_ = (dexVariables_ >> DSL.BITS_DEX_LITE_DEX_VARIABLES_TOKEN_1_DECIMALS) & X5;
if (token1Decimals_ > TOKENS_DECIMALS_PRECISION) {
amountOut_ /= _tenPow(token1Decimals_ - TOKENS_DECIMALS_PRECISION);
} else {
amountOut_ *= _tenPow(TOKENS_DECIMALS_PRECISION - token1Decimals_);
}
if (amountOut_ < FOUR_DECIMALS || amountOut_ > X60) {
revert InvalidSwapAmounts(amountOut_);
}
if (amountOut_ > (token1ImaginaryReserves_ / 2)) {
revert ExcessiveSwapAmount(amountOut_, token1ImaginaryReserves_);
}
// amountIn = (amountOut * iReserveIn) / (iReserveOut - amountOut)
amountIn_ = (amountOut_ * token0ImaginaryReserves_) / (token1ImaginaryReserves_ - amountOut_);
uint256 fee_ = ((amountIn_ * SIX_DECIMALS) / (SIX_DECIMALS - ((dexVariables_ >> DSL.BITS_DEX_LITE_DEX_VARIABLES_FEE) & X13))) - amountIn_;
amountIn_ += fee_;
token0AdjustedSupply_ += amountIn_ - ((fee_ * ((dexVariables_ >> DSL.BITS_DEX_LITE_DEX_VARIABLES_REVENUE_CUT) & X7)) / TWO_DECIMALS);
if (token1AdjustedSupply_ < amountOut_) {
revert TokenReservesTooLow(amountOut_, token1AdjustedSupply_);
}
token1AdjustedSupply_ -= amountOut_;
if (((token1AdjustedSupply_) < ((token0AdjustedSupply_ * centerPrice_) / (PRICE_PRECISION * MINIMUM_LIQUIDITY_SWAP)))) {
revert TokenReservesRatioTooHigh(token0AdjustedSupply_, token1AdjustedSupply_);
}
} else {
uint256 token0Decimals_ = (dexVariables_ >> DSL.BITS_DEX_LITE_DEX_VARIABLES_TOKEN_0_DECIMALS) & X5;
if (token0Decimals_ > TOKENS_DECIMALS_PRECISION) {
amountOut_ /= _tenPow(token0Decimals_ - TOKENS_DECIMALS_PRECISION);
} else {
amountOut_ *= _tenPow(TOKENS_DECIMALS_PRECISION - token0Decimals_);
}
if (amountOut_ < FOUR_DECIMALS || amountOut_ > X60) {
revert InvalidSwapAmounts(amountOut_);
}
if (amountOut_ > (token0ImaginaryReserves_ / 2)) {
revert ExcessiveSwapAmount(amountOut_, token0ImaginaryReserves_);
}
// amountIn = (amountOut * iReserveIn) / (iReserveOut - amountOut)
amountIn_ = (amountOut_ * token1ImaginaryReserves_) / (token0ImaginaryReserves_ - amountOut_);
uint256 fee_ = ((amountIn_ * SIX_DECIMALS) / (SIX_DECIMALS - ((dexVariables_ >> DSL.BITS_DEX_LITE_DEX_VARIABLES_FEE) & X13))) - amountIn_;
amountIn_ += fee_;
token1AdjustedSupply_ += amountIn_ - ((fee_ * ((dexVariables_ >> DSL.BITS_DEX_LITE_DEX_VARIABLES_REVENUE_CUT) & X7)) / TWO_DECIMALS);
if (token0AdjustedSupply_ < amountOut_) {
revert TokenReservesTooLow(amountOut_, token0AdjustedSupply_);
}
token0AdjustedSupply_ -= amountOut_;
if (((token0AdjustedSupply_) < ((token1AdjustedSupply_ * PRICE_PRECISION) / (centerPrice_ * MINIMUM_LIQUIDITY_SWAP)))) {
revert TokenReservesRatioTooHigh(token0AdjustedSupply_, token1AdjustedSupply_);
}
}
}
{
uint256 rebalancingStatus_ = (dexVariables_ >> DSL.BITS_DEX_LITE_DEX_VARIABLES_REBALANCING_STATUS) & X2;
if (rebalancingStatus_ > 0) {
// rebalancing is active
uint256 price_;
unchecked {
price_ = (swap0To1_ ? (token1ImaginaryReserves_ - amountOut_) * PRICE_PRECISION / (token0ImaginaryReserves_ + amountIn_) :
(token1ImaginaryReserves_ + amountIn_) * PRICE_PRECISION / (token0ImaginaryReserves_ - amountOut_));
}
rebalancingStatus_ = _getRebalancingStatus(dexVariables_, dexId_, rebalancingStatus_, price_, centerPrice_);
}
if (rebalancingStatus_ > 1 || ((dexVariables_ >> DSL.BITS_DEX_LITE_DEX_VARIABLES_CENTER_PRICE_SHIFT_ACTIVE) & X1) == 1) {
_centerPriceShift[dexId_] = _centerPriceShift[dexId_] & ~(X33 << DSL.BITS_DEX_LITE_CENTER_PRICE_SHIFT_LAST_INTERACTION_TIMESTAMP) |
(block.timestamp << DSL.BITS_DEX_LITE_CENTER_PRICE_SHIFT_LAST_INTERACTION_TIMESTAMP);
}
}
if (token0AdjustedSupply_ > X60 || token1AdjustedSupply_ > X60) {
revert AdjustedSupplyOverflow(dexId_, token0AdjustedSupply_, token1AdjustedSupply_);
}
dexVariables_ = _dexVariables[dexId_] & ~(X120 << DSL.BITS_DEX_LITE_DEX_VARIABLES_TOKEN_0_TOTAL_SUPPLY_ADJUSTED) |
(token0AdjustedSupply_ << DSL.BITS_DEX_LITE_DEX_VARIABLES_TOKEN_0_TOTAL_SUPPLY_ADJUSTED) |
(token1AdjustedSupply_ << DSL.BITS_DEX_LITE_DEX_VARIABLES_TOKEN_1_TOTAL_SUPPLY_ADJUSTED);
_dexVariables[dexId_] = dexVariables_;
if (swap0To1_) {
emit LogSwap(
(uint256(uint64(dexId_)) << DSL.BITS_DEX_LITE_SWAP_DATA_DEX_ID) |
(uint256(1) << DSL.BITS_DEX_LITE_SWAP_DATA_SWAP_0_TO_1) | // swap 0 to 1 bit is 1
(amountIn_ << DSL.BITS_DEX_LITE_SWAP_DATA_AMOUNT_IN) |
(amountOut_ << DSL.BITS_DEX_LITE_SWAP_DATA_AMOUNT_OUT),
dexVariables_
);
uint256 token0Decimals_ = (dexVariables_ >> DSL.BITS_DEX_LITE_DEX_VARIABLES_TOKEN_0_DECIMALS) & X5;
if (token0Decimals_ > TOKENS_DECIMALS_PRECISION) {
amountIn_ *= _tenPow(token0Decimals_ - TOKENS_DECIMALS_PRECISION);
} else {
amountIn_ /= _tenPow(TOKENS_DECIMALS_PRECISION - token0Decimals_);
}
} else {
emit LogSwap(
(uint256(uint64(dexId_)) << DSL.BITS_DEX_LITE_SWAP_DATA_DEX_ID) |
// swap 0 to 1 bit is 0
(amountIn_ << DSL.BITS_DEX_LITE_SWAP_DATA_AMOUNT_IN) |
(amountOut_ << DSL.BITS_DEX_LITE_SWAP_DATA_AMOUNT_OUT),
dexVariables_
);
uint256 token1Decimals_ = (dexVariables_ >> DSL.BITS_DEX_LITE_DEX_VARIABLES_TOKEN_1_DECIMALS) & X5;
if (token1Decimals_ > TOKENS_DECIMALS_PRECISION) {
amountIn_ *= _tenPow(token1Decimals_ - TOKENS_DECIMALS_PRECISION);
} else {
amountIn_ /= _tenPow(TOKENS_DECIMALS_PRECISION - token1Decimals_);
}
}
}
}// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.29; import "../other/commonImport.sol"; // --- Custom Errors --- error EstimateSwap(uint256 amountUnspecified); error AmountLimitExceeded(uint256 amountUnspecified, uint256 amountLimit); error AmountLimitNotMet(uint256 amountUnspecified, uint256 amountLimit); error EmptyDexKeysArray(); error InvalidPathLength(uint256 pathLength, uint256 dexKeysLength); error InvalidAmountLimitsLength(uint256 dexKeysLength, uint256 amountLimitsLength); error InvalidPathTokenOrder(); error UnauthorizedCaller(address caller); error DexNotInitialized(bytes32 dexId); error AdjustedSupplyOverflow(bytes32 dexId, uint256 token0AdjustedSupply, uint256 token1AdjustedSupply); error ZeroAddress(); error InvalidPower(uint256 power); error InvalidSwapAmounts(uint256 adjustedAmount); error ExcessiveSwapAmount(uint256 adjustedAmount, uint256 imaginaryReserve); error TokenReservesTooLow(uint256 adjustedAmount, uint256 realReserve); error TokenReservesRatioTooHigh(uint256 token0RealReserve, uint256 token1RealReserve); error InvalidMsgValue(); error InsufficientNativeTokenReceived(uint256 receivedAmount, uint256 requiredAmount); error InsufficientERC20Received(uint256 receivedAmount, uint256 requiredAmount); error DelegateCallFailed();
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.29;
import "./errors.sol";
import { DexLiteSlotsLink as DSL } from "../../../libraries/dexLiteSlotsLink.sol";
import { FixedPointMathLib } from "solmate/src/utils/FixedPointMathLib.sol";
import { AddressCalcs } from "../../../libraries/addressCalcs.sol";
import { BigMathMinified } from "../../../libraries/bigMathMinified.sol";
import { ReentrancyLock } from "../../../libraries/reentrancyLock.sol";
import { SafeTransfer } from "../../../libraries/safeTransfer.sol";
abstract contract Helpers is CommonImport {
using BigMathMinified for uint256;
modifier _reentrancyLock() {
ReentrancyLock.lock();
_;
ReentrancyLock.unlock();
}
function _getExtraDataSlot() internal view returns (address extraDataAddress_) {
assembly {
extraDataAddress_ := sload(EXTRA_DATA_SLOT)
}
}
function _getGovernanceAddr() internal view returns (address governance_) {
governance_ = address(uint160(LIQUIDITY.readFromStorage(LIQUIDITY_GOVERNANCE_SLOT)));
}
function _callExtraDataSlot(bytes memory data_) internal {
address extraDataAddress_ = _getExtraDataSlot();
if (extraDataAddress_ == address(0)) {
revert ZeroAddress();
}
_spell(extraDataAddress_, data_);
}
function _tenPow(uint256 power_) internal pure returns (uint256) {
// keeping the most used powers at the top for better gas optimization
if (power_ == 3) {
return 1_000; // used for 6 or 12 decimals (USDC, USDT)
}
if (power_ == 9) {
return 1_000_000_000; // used for 18 decimals (ETH, and many more)
}
if (power_ == 1) {
return 10; // used for 1 decimals (WBTC and more)
}
if (power_ == 0) {
return 1;
}
if (power_ == 2) {
return 100;
}
if (power_ == 4) {
return 10_000;
}
if (power_ == 5) {
return 100_000;
}
if (power_ == 6) {
return 1_000_000;
}
if (power_ == 7) {
return 10_000_000;
}
if (power_ == 8) {
return 100_000_000;
}
// We will only need powers from 0 to 9 as token decimals can only be 6 to 18
revert InvalidPower(power_);
}
/// @dev getting reserves outside range.
/// @param gp_ is geometric mean pricing of upper percent & lower percent
/// @param pa_ price of upper range or lower range
/// @param rx_ real reserves of token0 or token1
/// @param ry_ whatever is rx_ the other will be ry_
function _calculateReservesOutsideRange(uint256 gp_, uint256 pa_, uint256 rx_, uint256 ry_) internal pure returns (uint256 xa_, uint256 yb_) {
// equations we have:
// 1. x*y = k
// 2. xa*ya = k
// 3. xb*yb = k
// 4. Pa = ya / xa = upperRange_ (known)
// 5. Pb = yb / xb = lowerRange_ (known)
// 6. x - xa = rx = real reserve of x (known)
// 7. y - yb = ry = real reserve of y (known)
// With solving we get:
// ((Pa*Pb)^(1/2) - Pa)*xa^2 + (rx * (Pa*Pb)^(1/2) + ry)*xa + rx*ry = 0
// yb = yb = xa * (Pa * Pb)^(1/2)
// xa = (GP⋅rx + ry + (-rx⋅ry⋅4⋅(GP - Pa) + (GP⋅rx + ry)^2)^0.5) / (2Pa - 2GP)
// multiply entire equation by 1e27 to remove the price decimals precision of 1e27
// xa = (GP⋅rx + ry⋅1e27 + (rx⋅ry⋅4⋅(Pa - GP)⋅1e27 + (GP⋅rx + ry⋅1e27)^2)^0.5) / 2*(Pa - GP)
// dividing the equation with 2*(Pa - GP). Pa is always > GP so answer will be positive.
// xa = (((GP⋅rx + ry⋅1e27) / 2*(Pa - GP)) + (((rx⋅ry⋅4⋅(Pa - GP)⋅1e27) / 4*(Pa - GP)^2) + ((GP⋅rx + ry⋅1e27) / 2*(Pa - GP))^2)^0.5)
// xa = (((GP⋅rx + ry⋅1e27) / 2*(Pa - GP)) + (((rx⋅ry⋅1e27) / (Pa - GP)) + ((GP⋅rx + ry⋅1e27) / 2*(Pa - GP))^2)^0.5)
// dividing in 3 parts for simplification:
// part1 = (Pa - GP)
// part2 = (GP⋅rx + ry⋅1e27) / (2*part1)
// part3 = rx⋅ry
// note: part1 will almost always be < 1e28 but in case it goes above 1e27 then it's extremely unlikely it'll go above > 1e29
unchecked {
uint256 p1_ = pa_ - gp_;
uint256 p2_ = ((gp_ * rx_) + (ry_ * PRICE_PRECISION)) / (2 * p1_);
// removed <1e50 check becuase rx_ * ry_ will never be greater than 1e50
// Directly used p3_ below instead of using a variable for it
// uint256 p3_ = (rx_ * ry_ * PRICE_PRECISION) / p1_;
// xa = part2 + (part3 + (part2 * part2))^(1/2)
// yb = xa_ * gp_
xa_ = p2_ + FixedPointMathLib.sqrt((((rx_ * ry_ * PRICE_PRECISION) / p1_) + (p2_ * p2_)));
yb_ = (xa_ * gp_) / PRICE_PRECISION;
}
}
/// @dev This function calculates the new value of a parameter after a shifting process
/// @param current_ The current value is the final value where the shift ends
/// @param old_ The old value from where shifting started
/// @param timePassed_ The time passed since shifting started
/// @param shiftDuration_ The total duration of the shift when old_ reaches current_
/// @return The new value of the parameter after the shift
function _calcShiftingDone(uint256 current_, uint256 old_, uint256 timePassed_, uint256 shiftDuration_) internal pure returns (uint256) {
unchecked {
if (current_ > old_) {
return (old_ + (((current_ - old_) * timePassed_) / shiftDuration_));
} else {
return (old_ - (((old_ - current_) * timePassed_) / shiftDuration_));
}
}
}
/// @dev Calculates the new upper and lower range values during an active range shift
/// @param upperRange_ The target upper range value
/// @param lowerRange_ The target lower range value
/// @notice This function handles the gradual shifting of range values over time
/// @notice If the shift is complete, it updates the state and clears the shift data
function _calcRangeShifting(
uint256 upperRange_,
uint256 lowerRange_,
bytes8 dexId_
) internal returns (uint256, uint256) {
uint256 rangeShift_ = _rangeShift[dexId_];
uint256 shiftDuration_ = (rangeShift_ >> DSL.BITS_DEX_LITE_RANGE_SHIFT_TIME_TO_SHIFT) & X20;
uint256 startTimeStamp_ = (rangeShift_ >> DSL.BITS_DEX_LITE_RANGE_SHIFT_TIMESTAMP) & X33;
uint256 timePassed_;
unchecked {
if ((startTimeStamp_ + shiftDuration_) < block.timestamp) {
// shifting fully done
delete _rangeShift[dexId_];
// making active shift as 0 because shift is over
// fetching from storage and storing in storage, aside from admin module dexVariables only updates from this function and _calcThresholdShifting.
_dexVariables[dexId_] = _dexVariables[dexId_] & ~uint256(1 << DSL.BITS_DEX_LITE_DEX_VARIABLES_RANGE_PERCENT_SHIFT_ACTIVE);
return (upperRange_, lowerRange_);
}
timePassed_ = block.timestamp - startTimeStamp_;
}
return (
_calcShiftingDone(upperRange_, (rangeShift_ >> DSL.BITS_DEX_LITE_RANGE_SHIFT_OLD_UPPER_RANGE_PERCENT) & X14, timePassed_, shiftDuration_),
_calcShiftingDone(lowerRange_, (rangeShift_ >> DSL.BITS_DEX_LITE_RANGE_SHIFT_OLD_LOWER_RANGE_PERCENT) & X14, timePassed_, shiftDuration_)
);
}
/// @dev Calculates the new upper and lower threshold values during an active threshold shift
/// @param upperThreshold_ The target upper threshold value
/// @param lowerThreshold_ The target lower threshold value
/// @return The updated upper threshold, lower threshold
/// @notice This function handles the gradual shifting of threshold values over time
/// @notice If the shift is complete, it updates the state and clears the shift data
function _calcThresholdShifting(
uint256 upperThreshold_,
uint256 lowerThreshold_,
bytes8 dexId_
) internal returns (uint256, uint256) {
uint256 thresholdShift_ = _thresholdShift[dexId_];
uint256 shiftDuration_ = (thresholdShift_ >> DSL.BITS_DEX_LITE_THRESHOLD_SHIFT_TIME_TO_SHIFT) & X20;
uint256 startTimeStamp_ = (thresholdShift_ >> DSL.BITS_DEX_LITE_THRESHOLD_SHIFT_TIMESTAMP) & X33;
uint256 timePassed_;
unchecked {
if ((startTimeStamp_ + shiftDuration_) < block.timestamp) {
// shifting fully done
delete _thresholdShift[dexId_];
// making active shift as 0 because shift is over
// fetching from storage and storing in storage, aside from admin module dexVariables2 only updates from this function and _calcRangeShifting.
_dexVariables[dexId_] = _dexVariables[dexId_] & ~uint256(1 << DSL.BITS_DEX_LITE_DEX_VARIABLES_THRESHOLD_PERCENT_SHIFT_ACTIVE);
return (upperThreshold_, lowerThreshold_);
}
timePassed_ = block.timestamp - startTimeStamp_;
}
return (
_calcShiftingDone(upperThreshold_, (thresholdShift_ >> DSL.BITS_DEX_LITE_THRESHOLD_SHIFT_OLD_UPPER_THRESHOLD_PERCENT) & X7, timePassed_, shiftDuration_),
_calcShiftingDone(lowerThreshold_, (thresholdShift_ >> DSL.BITS_DEX_LITE_THRESHOLD_SHIFT_OLD_LOWER_THRESHOLD_PERCENT) & X7, timePassed_, shiftDuration_)
);
}
/// @dev Calculates the new center price during an active price shift
/// @param dexVariables_ The current state of dex variables
/// @return newCenterPrice_ The updated center price
/// @notice This function gradually shifts the center price towards a new target price over time
/// @notice It uses an external price source (via ICenterPrice) to determine the target price
/// @notice The shift continues until the current price reaches the target, or the shift duration ends
/// @notice Once the shift is complete, it updates the state and clears the shift data
/// @notice The shift rate is dynamic and depends on:
/// @notice - Time remaining in the shift duration
/// @notice - The new center price (fetched externally, which may change)
/// @notice - The current (old) center price
/// @notice This results in a fuzzy shifting mechanism where the rate can change as these parameters evolve
/// @notice The externally fetched new center price is expected to not differ significantly from the last externally fetched center price
function _calcCenterPrice(
DexKey calldata dexKey_,
uint256 dexVariables_,
bytes8 dexId_
) internal returns (uint256 newCenterPrice_) {
uint256 oldCenterPrice_ = (dexVariables_ >> DSL.BITS_DEX_LITE_DEX_VARIABLES_CENTER_PRICE) & X40;
oldCenterPrice_ = (oldCenterPrice_ >> DEFAULT_EXPONENT_SIZE) << (oldCenterPrice_ & DEFAULT_EXPONENT_MASK);
uint256 centerPriceShift_ = _centerPriceShift[dexId_];
uint256 startTimeStamp_ = (centerPriceShift_ >> DSL.BITS_DEX_LITE_CENTER_PRICE_SHIFT_TIMESTAMP) & X33;
uint256 fromTimeStamp_ = (centerPriceShift_ >> DSL.BITS_DEX_LITE_CENTER_PRICE_SHIFT_LAST_INTERACTION_TIMESTAMP) & X33;
fromTimeStamp_ = fromTimeStamp_ > startTimeStamp_ ? fromTimeStamp_ : startTimeStamp_;
newCenterPrice_ = ICenterPrice(
AddressCalcs.addressCalc(DEPLOYER_CONTRACT, ((dexVariables_ >> DSL.BITS_DEX_LITE_DEX_VARIABLES_CENTER_PRICE_CONTRACT_ADDRESS) & X19)))
.centerPrice(dexKey_.token0, dexKey_.token1);
unchecked {
uint256 priceShift_ = (oldCenterPrice_ * ((centerPriceShift_ >> DSL.BITS_DEX_LITE_CENTER_PRICE_SHIFT_PERCENT) & X20) * (block.timestamp - fromTimeStamp_))
/ (((centerPriceShift_ >> DSL.BITS_DEX_LITE_CENTER_PRICE_SHIFT_TIME_TO_SHIFT) & X20) * SIX_DECIMALS);
if (newCenterPrice_ > oldCenterPrice_) {
// shift on positive side
oldCenterPrice_ += priceShift_;
if (newCenterPrice_ > oldCenterPrice_) {
newCenterPrice_ = oldCenterPrice_;
} else {
// shifting fully done
_centerPriceShift[dexId_] = _centerPriceShift[dexId_] & ~(X73 << DSL.BITS_DEX_LITE_CENTER_PRICE_SHIFT_PERCENT);
// making active shift as 0 because shift is over
// fetching from storage and storing in storage, aside from admin module dexVariables2 only updates these shift function.
_dexVariables[dexId_] = _dexVariables[dexId_] & ~uint256(1 << DSL.BITS_DEX_LITE_DEX_VARIABLES_CENTER_PRICE_SHIFT_ACTIVE);
}
} else {
oldCenterPrice_ = oldCenterPrice_ > priceShift_ ? oldCenterPrice_ - priceShift_ : 0;
// In case of oldCenterPrice_ ending up 0, which could happen when a lot of time has passed (pool has no swaps for many days or weeks)
// then below we get into the else logic which will fully conclude shifting and return newCenterPrice_
// as it was fetched from the external center price source.
// not ideal that this would ever happen unless the pool is not in use and all/most users have left leaving not enough liquidity to trade on
if (newCenterPrice_ < oldCenterPrice_) {
newCenterPrice_ = oldCenterPrice_;
} else {
// shifting fully done
_centerPriceShift[dexId_] = _centerPriceShift[dexId_] & ~(X73 << DSL.BITS_DEX_LITE_CENTER_PRICE_SHIFT_PERCENT);
// making active shift as 0 because shift is over
// fetching from storage and storing in storage, aside from admin module dexVariables2 only updates these shift function.
_dexVariables[dexId_] = _dexVariables[dexId_] & ~uint256(1 << DSL.BITS_DEX_LITE_DEX_VARIABLES_CENTER_PRICE_SHIFT_ACTIVE);
}
}
}
}
/// @notice Calculates and returns the current prices and exchange prices for the pool
/// @param dexVariables_ The first set of DEX variables containing various pool parameters
function _getPricesAndReserves(
DexKey calldata dexKey_,
uint256 dexVariables_,
bytes8 dexId_,
uint256 token0Supply_,
uint256 token1Supply_
) internal returns (uint256 centerPrice_, uint256 token0ImaginaryReserves_, uint256 token1ImaginaryReserves_) {
// Fetch center price
if (((dexVariables_ >> DSL.BITS_DEX_LITE_DEX_VARIABLES_CENTER_PRICE_SHIFT_ACTIVE) & X1) == 0) {
// centerPrice_ => center price nonce
centerPrice_ = (dexVariables_ >> DSL.BITS_DEX_LITE_DEX_VARIABLES_CENTER_PRICE_CONTRACT_ADDRESS) & X19;
if (centerPrice_ == 0) {
centerPrice_ = (dexVariables_ >> DSL.BITS_DEX_LITE_DEX_VARIABLES_CENTER_PRICE) & X40;
centerPrice_ = (centerPrice_ >> DEFAULT_EXPONENT_SIZE) << (centerPrice_ & DEFAULT_EXPONENT_MASK);
} else {
// center price should be fetched from external source. For exmaple, in case of wstETH <> ETH pool,
// we would want the center price to be pegged to wstETH exchange rate into ETH
centerPrice_ =
ICenterPrice(AddressCalcs.addressCalc(DEPLOYER_CONTRACT, centerPrice_)).centerPrice(dexKey_.token0, dexKey_.token1);
}
} else {
// an active centerPrice_ shift is going on
centerPrice_ = _calcCenterPrice(dexKey_, dexVariables_, dexId_);
}
uint256 upperRangePercent_ = (dexVariables_ >> DSL.BITS_DEX_LITE_DEX_VARIABLES_UPPER_PERCENT) & X14;
uint256 lowerRangePercent_ = (dexVariables_ >> DSL.BITS_DEX_LITE_DEX_VARIABLES_LOWER_PERCENT) & X14;
if (((dexVariables_ >> DSL.BITS_DEX_LITE_DEX_VARIABLES_RANGE_PERCENT_SHIFT_ACTIVE) & X1) == 1) {
// an active range shift is going on
(upperRangePercent_, lowerRangePercent_) = _calcRangeShifting(upperRangePercent_, lowerRangePercent_, dexId_);
}
uint256 upperRangePrice_;
uint256 lowerRangePrice_;
unchecked {
// 1% = 1e2, 100% = 1e4
upperRangePrice_ = (centerPrice_ * FOUR_DECIMALS) / (FOUR_DECIMALS - upperRangePercent_);
// 1% = 1e2, 100% = 1e4
lowerRangePrice_ = (centerPrice_ * (FOUR_DECIMALS - lowerRangePercent_)) / FOUR_DECIMALS;
}
// Rebalance center price if rebalancing is on
// temp_ => rebalancingStatus_
uint256 temp_ = (dexVariables_ >> DSL.BITS_DEX_LITE_DEX_VARIABLES_REBALANCING_STATUS) & X2;
uint256 temp2_;
if (temp_ > 1) {
unchecked {
// temp2_ => centerPriceShift_
if (temp_ == 2) {
temp2_ = _centerPriceShift[dexId_];
uint256 shiftingTime_ = (temp2_ >> DSL.BITS_DEX_LITE_CENTER_PRICE_SHIFT_SHIFTING_TIME) & X24;
uint256 timeElapsed_ = block.timestamp - ((temp2_ >> DSL.BITS_DEX_LITE_CENTER_PRICE_SHIFT_LAST_INTERACTION_TIMESTAMP) & X33);
// price shifting towards upper range
if (timeElapsed_ < shiftingTime_) {
centerPrice_ = centerPrice_ + (((upperRangePrice_ - centerPrice_) * timeElapsed_) / shiftingTime_);
} else {
// 100% price shifted
centerPrice_ = upperRangePrice_;
}
} else if (temp_ == 3) {
temp2_ = _centerPriceShift[dexId_];
uint256 shiftingTime_ = (temp2_ >> DSL.BITS_DEX_LITE_CENTER_PRICE_SHIFT_SHIFTING_TIME) & X24;
uint256 timeElapsed_ = block.timestamp - ((temp2_ >> DSL.BITS_DEX_LITE_CENTER_PRICE_SHIFT_LAST_INTERACTION_TIMESTAMP) & X33);
// price shifting towards lower range
if (timeElapsed_ < shiftingTime_) {
centerPrice_ = centerPrice_ - (((centerPrice_ - lowerRangePrice_) * timeElapsed_) / shiftingTime_);
} else {
// 100% price shifted
centerPrice_ = lowerRangePrice_;
}
}
// If rebalancing actually happened then make sure price is within min and max bounds, and update range prices
if (temp2_ > 0) {
// Make sure center price is within min and max bounds
// temp_ => max center price
temp_ = (temp2_ >> DSL.BITS_DEX_LITE_CENTER_PRICE_SHIFT_MAX_CENTER_PRICE) & X28;
temp_ = (temp_ >> DEFAULT_EXPONENT_SIZE) << (temp_ & DEFAULT_EXPONENT_MASK);
if (centerPrice_ > temp_) {
// if center price is greater than max center price
centerPrice_ = temp_;
} else {
// check if center price is less than min center price
// temp_ => min center price
temp_ = (temp2_ >> DSL.BITS_DEX_LITE_CENTER_PRICE_SHIFT_MIN_CENTER_PRICE) & X28;
temp_ = (temp_ >> DEFAULT_EXPONENT_SIZE) << (temp_ & DEFAULT_EXPONENT_MASK);
if (centerPrice_ < temp_) centerPrice_ = temp_;
}
// Update range prices as center price moved
upperRangePrice_ = (centerPrice_ * FOUR_DECIMALS) / (FOUR_DECIMALS - upperRangePercent_);
lowerRangePrice_ = (centerPrice_ * (FOUR_DECIMALS - lowerRangePercent_)) / FOUR_DECIMALS;
}
}
}
// temp_ => geometricMeanPrice_
unchecked {
if (upperRangePrice_ < 1e38) {
// 1e38 * 1e38 = 1e76 which is less than max uint limit
temp_ = FixedPointMathLib.sqrt(upperRangePrice_ * lowerRangePrice_);
} else {
// upperRange_ price is pretty large hence lowerRange_ will also be pretty large
temp_ = FixedPointMathLib.sqrt((upperRangePrice_ / 1e18) * (lowerRangePrice_ / 1e18)) * 1e18;
}
}
if (temp_ < 1e27) {
(token0ImaginaryReserves_, token1ImaginaryReserves_) =
_calculateReservesOutsideRange(temp_, upperRangePrice_, token0Supply_, token1Supply_);
} else {
// inversing, something like `xy = k` so for calculation we are making everything related to x into y & y into x
// 1 / geometricMean for new geometricMean
// 1 / lowerRange will become upper range
// 1 / upperRange will become lower range
unchecked {
(token1ImaginaryReserves_, token0ImaginaryReserves_) = _calculateReservesOutsideRange(
(1e54 / temp_),
(1e54 / lowerRangePrice_),
token1Supply_,
token0Supply_
);
}
}
unchecked {
token0ImaginaryReserves_ += token0Supply_;
token1ImaginaryReserves_ += token1Supply_;
}
}
function _getRebalancingStatus(
uint256 dexVariables_,
bytes8 dexId_,
uint256 rebalancingStatus_,
uint256 price_,
uint256 centerPrice_
) internal returns (uint256) {
uint256 upperRange_ = (dexVariables_ >> DSL.BITS_DEX_LITE_DEX_VARIABLES_UPPER_PERCENT) & X14;
uint256 lowerRange_ = (dexVariables_ >> DSL.BITS_DEX_LITE_DEX_VARIABLES_LOWER_PERCENT) & X14;
// NOTE: we are using dexVariables_ and not _dexVariables[dexId_] here to check if the range shift is active
// range shift might have already ended in this transaction above, but still calling _calcRangeShifting again because we don't want to use _dexVariables[dexId_] here because of gas
if (((dexVariables_ >> DSL.BITS_DEX_LITE_DEX_VARIABLES_RANGE_PERCENT_SHIFT_ACTIVE) & X1) == 1) {
// an active range shift is going on
(upperRange_, lowerRange_) = _calcRangeShifting(upperRange_, lowerRange_, dexId_);
}
unchecked {
// adding into unchecked because upperRangePercent_ & lowerRangePercent_ can only be > 0 & < FOUR_DECIMALS
// 1% = 1e2, 100% = 1e4
upperRange_ = (centerPrice_ * FOUR_DECIMALS) / (FOUR_DECIMALS - upperRange_);
// 1% = 1e2, 100% = 1e4
lowerRange_ = (centerPrice_ * (FOUR_DECIMALS - lowerRange_)) / FOUR_DECIMALS;
}
// Calculate threshold prices
uint256 upperThreshold_ = (dexVariables_ >> DSL.BITS_DEX_LITE_DEX_VARIABLES_UPPER_SHIFT_THRESHOLD_PERCENT) & X7;
uint256 lowerThreshold_ = (dexVariables_ >> DSL.BITS_DEX_LITE_DEX_VARIABLES_LOWER_SHIFT_THRESHOLD_PERCENT) & X7;
if (((dexVariables_ >> DSL.BITS_DEX_LITE_DEX_VARIABLES_THRESHOLD_PERCENT_SHIFT_ACTIVE) & X1) == 1) {
// if active shift is going on for threshold then calculate threshold real time
(upperThreshold_, lowerThreshold_) = _calcThresholdShifting(upperThreshold_, lowerThreshold_, dexId_);
}
unchecked {
upperThreshold_ =
(centerPrice_ + ((upperRange_ - centerPrice_) * (TWO_DECIMALS - upperThreshold_)) / TWO_DECIMALS);
lowerThreshold_ =
(centerPrice_ - ((centerPrice_ - lowerRange_) * (TWO_DECIMALS - lowerThreshold_)) / TWO_DECIMALS);
}
if (price_ > upperThreshold_) {
if (rebalancingStatus_ != 2) {
_dexVariables[dexId_] = _dexVariables[dexId_] & ~(X2 << DSL.BITS_DEX_LITE_DEX_VARIABLES_REBALANCING_STATUS) |
(2 << DSL.BITS_DEX_LITE_DEX_VARIABLES_REBALANCING_STATUS);
return 2;
}
} else if (price_ < lowerThreshold_) {
if (rebalancingStatus_ != 3) {
_dexVariables[dexId_] = _dexVariables[dexId_] & ~(X2 << DSL.BITS_DEX_LITE_DEX_VARIABLES_REBALANCING_STATUS) |
(3 << DSL.BITS_DEX_LITE_DEX_VARIABLES_REBALANCING_STATUS);
return 3;
}
} else {
if (rebalancingStatus_ != 1) {
_dexVariables[dexId_] = _dexVariables[dexId_] & ~(X2 << DSL.BITS_DEX_LITE_DEX_VARIABLES_REBALANCING_STATUS) |
(1 << DSL.BITS_DEX_LITE_DEX_VARIABLES_REBALANCING_STATUS);
return 1;
}
}
return rebalancingStatus_;
}
function _transferTokens(
address tokenIn_,
uint256 amountIn_,
address tokenOut_,
uint256 amountOut_,
address to_,
bool isCallback_,
bytes calldata callbackData_
) internal {
if (to_ == address(0)) {
to_ = msg.sender;
}
// Transfer tokens out first
if (tokenOut_ == NATIVE_TOKEN) {
SafeTransfer.safeTransferNative(to_, amountOut_);
} else {
SafeTransfer.safeTransfer(tokenOut_, to_, amountOut_);
}
// Transfer tokens in
if (tokenIn_ == NATIVE_TOKEN) {
if (isCallback_ && msg.value == 0) {
uint256 ethBalance_ = address(this).balance;
IDexLiteCallback(msg.sender).dexCallback(tokenIn_, amountIn_, callbackData_);
if (address(this).balance - ethBalance_ < amountIn_) revert InsufficientNativeTokenReceived(address(this).balance - ethBalance_, amountIn_);
} else {
if (msg.value < amountIn_) {
revert InsufficientNativeTokenReceived(msg.value, amountIn_);
}
if (msg.value > amountIn_) {
SafeTransfer.safeTransferNative(msg.sender, msg.value - amountIn_);
}
// if msg.value == amountIn_ then that means the transfer has already happened
}
} else {
if (msg.value > 0) {
revert InvalidMsgValue(); // msg.value should be 0 for non native tokens
}
if (isCallback_) {
uint256 tokenInBalance_ = IERC20(tokenIn_).balanceOf(address(this));
IDexLiteCallback(msg.sender).dexCallback(tokenIn_, amountIn_, callbackData_);
if ((IERC20(tokenIn_).balanceOf(address(this)) - tokenInBalance_) < amountIn_) {
revert InsufficientERC20Received(IERC20(tokenIn_).balanceOf(address(this)) - tokenInBalance_, amountIn_);
}
} else {
SafeTransfer.safeTransferFrom(tokenIn_, msg.sender, address(this), amountIn_);
}
}
}
/// @dev do any arbitrary call
/// @param target_ Address to which the call needs to be delegated
/// @param data_ Data to execute at the delegated address
function _spell(address target_, bytes memory data_) internal returns (bytes memory response_) {
assembly {
let succeeded := delegatecall(gas(), target_, add(data_, 0x20), mload(data_), 0, 0)
let size := returndatasize()
response_ := mload(0x40)
mstore(0x40, add(response_, and(add(add(size, 0x20), 0x1f), not(0x1f))))
mstore(response_, size)
returndatacopy(add(response_, 0x20), 0, size)
if iszero(succeeded) {
// throw if delegatecall failed
returndatacopy(0x00, 0x00, size)
revert(0x00, size)
}
}
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.29;
import "./variables.sol";
abstract contract CommonImport is Variables {}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.29;
import "./events.sol";
abstract contract ConstantVariables {
/*//////////////////////////////////////////////////////////////
CONSTANTS
//////////////////////////////////////////////////////////////*/
/// bytes32(uint256(keccak256("FLUID_DEX_LITE_EXTRA_DATA")) - 1)
bytes32 internal constant EXTRA_DATA_SLOT = 0x7e8134afb5ed35d36cb65e24b9a4712a52bb77d952806c1acf50970d2107797f;
/// This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1
/// The exact slot which stored the admin address in infinite proxy of liquidity contracts
bytes32 internal constant LIQUIDITY_GOVERNANCE_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
bool internal constant SWAP_SINGLE = true;
bool internal constant SWAP_HOP = false;
address internal constant NATIVE_TOKEN = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
uint256 internal constant NATIVE_TOKEN_DECIMALS = 18;
uint256 internal constant TOKENS_DECIMALS_PRECISION = 9;
uint8 internal constant MIN_TOKEN_DECIMALS = 6;
uint8 internal constant MAX_TOKEN_DECIMALS = 18;
uint256 internal constant SMALL_COEFFICIENT_SIZE = 20;
uint256 internal constant BIG_COEFFICIENT_SIZE = 32;
uint256 internal constant DEFAULT_EXPONENT_SIZE = 8;
uint256 internal constant DEFAULT_EXPONENT_MASK = 0xFF;
uint256 internal constant X1 = 0x1;
uint256 internal constant X2 = 0x3;
uint256 internal constant X5 = 0x1f;
uint256 internal constant X7 = 0x7f;
uint256 internal constant X13 = 0x1fff;
uint256 internal constant X14 = 0x3fff;
uint256 internal constant X19 = 0x7ffff;
uint256 internal constant X20 = 0xfffff;
uint256 internal constant X24 = 0xffffff;
uint256 internal constant X28 = 0xfffffff;
uint256 internal constant X33 = 0x1ffffffff;
uint256 internal constant X40 = 0xffffffffff;
uint256 internal constant X56 = 0xffffffffffffff;
uint256 internal constant X60 = 0xfffffffffffffff;
uint256 internal constant X73 = 0x1ffffffffffffffffff;
uint256 internal constant X120 = 0xffffffffffffffffffffffffffffff;
uint256 internal constant X128 = 0xffffffffffffffffffffffffffffffff;
uint256 internal constant TWO_DECIMALS = 1e2;
uint256 internal constant FOUR_DECIMALS = 1e4;
uint256 internal constant SIX_DECIMALS = 1e6;
uint256 internal constant PRICE_PRECISION = 1e27;
/// after swap token0 reserves should not be less than token1InToken0 / MINIMUM_LIQUIDITY_SWAP
/// after swap token1 reserves should not be less than token0InToken1 / MINIMUM_LIQUIDITY_SWAP
uint256 internal constant MINIMUM_LIQUIDITY_SWAP = 1e4;
bytes32 internal constant ESTIMATE_SWAP = keccak256(bytes("ESTIMATE_SWAP"));
}// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.29; import "./interfaces.sol"; event LogSwap(uint256 swapData, uint256 dexVariables); // swapData // First 64 bits => 0 - 63 => dexId // Next 1 bit => 64 => swap 0 to 1 (1 => true, 0 => false) // Next 60 bits => 65 - 124 => amount in adjusted // Next 60 bits => 125 - 184 => amount out adjusted // dexVariables // Same as variables.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.29;
import "./constantVariables.sol";
// TODO
// import { IFluidDexFactory } from "../../interfaces/iDexFactory.sol";
// import { Error } from "../../error.sol";
// import { ErrorTypes } from "../../errorTypes.sol";
abstract contract ImmutableVariables is ConstantVariables {
/*//////////////////////////////////////////////////////////////
IMMUTABLES
//////////////////////////////////////////////////////////////*/
/// @dev Address of liquidity contract
IFluidLiquidity internal immutable LIQUIDITY;
/// @dev Address of contract used for deploying center price & hook related contract
address internal immutable DEPLOYER_CONTRACT;
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.29;
import { IERC20 } from "lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
import { IFluidLiquidity } from "../../../liquidity/interfaces/iLiquidity.sol";
import "./structs.sol";
interface IERC20WithDecimals is IERC20 {
function decimals() external view returns (uint8);
}
interface IDexLiteCallback {
function dexCallback(address token_, uint256 amount_, bytes calldata data_) external;
}
interface ICenterPrice {
/// @notice Retrieves the center price for the pool
/// @dev This function is marked as non-constant (potentially state-changing) to allow flexibility in price fetching mechanisms.
/// While typically used as a read-only operation, this design permits write operations if needed for certain token pairs
/// (e.g., fetching up-to-date exchange rates that may require state changes).
/// @return price The current price of token0 in terms of token1, expressed with 27 decimal places
function centerPrice(address token0_, address token1_) external returns (uint256);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.29;
import "./events.sol";
struct DexKey {
address token0;
address token1;
bytes32 salt;
}
struct TransferParams {
address to;
bool isCallback;
bytes callbackData;
bytes extraData;
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.29;
import "./immutableVariables.sol";
abstract contract Variables is ImmutableVariables {
/// @dev admin address
mapping(address => uint256) internal _isAuth;
/// @dev dexes list
DexKey[] internal _dexesList;
// First 13 bits => 0 - 12 => fee (1% = 10000, max value: 8191 = .8191%)
// Next 7 bits => 13 - 19 => revenue cut (1 = 1%)
// Next 2 bit => 20 - 21 => rebalancing status (0 = off, 1 = on but not active, 2 = rebalancing active towards upper range, 3 = rebalancing active towards lower range)
// Next 1 bit => 22 => is center price shift active
// Next 40 bits => 23 - 62 => center price. Center price from where the ranges will be calculated. BigNumber (32 bits precision, 8 bits exponent)
// Next 19 bits => 63 - 81 => center price contract address (Deployment Factory Nonce)
// Next 1 bit => 82 => range percent shift active or not, 0 = false, 1 = true, if true than that means governance has updated the below percents and the update should happen with a specified time
// Next 14 bits => 83 - 96 => upperPercent (1% = 100) upperRange - upperRange * upperPercent = centerPrice. Hence, upperRange = centerPrice / (1 - upperPercent)
// Next 14 bits => 97 - 110 => lowerPercent (1% = 100) lowerRange = centerPrice - centerPrice * lowerPercent
// Next 1 bit => 111 => threshold percent shift active or not, 0 = false, 1 = true, if true than that means governance has updated the below percents and the update should happen with a specified time
// Next 7 bits => 112 - 118 => upper shift threshold percent, 1 = 1%. 100 = 100%. if currentPrice > (centerPrice + (upperRange - centerPrice) * (100 - upperShiftThresholdPercent) / 100) then trigger shift
// Next 7 bits => 119 - 125 => lower shift threshold percent, 1 = 1%. 100 = 100%. if currentPrice < (centerPrice - (centerPrice - lowerRange) * (100 - lowerShiftThresholdPercent) / 100) then trigger shift
// Next 5 bits => 126 - 130 => token 0 decimals
// Next 5 bits => 131 - 135 => token 1 decimals
// Next 60 bits => 136 - 195 => total token 0 adjusted amount
// Next 60 bits => 196 - 255 => total token 1 adjusted amount
/// @dev dex id => dex variables
mapping(bytes8 => uint256) internal _dexVariables;
/// NOTE: Center price shift is always fuzzy, and can shift because of rebalancing or center price shift
// First 33 bits => 0 - 32 => last interaction timestamp (only stored when either rebalancing or center price shift is active)
/// REBALANCING RELATED THINGS
// First 24 bits => 33 - 56 => shifting time (max ~194 days)
// Next 28 bits => 57 - 84 => max center price. BigNumber (20 bits precision, 8 bits exponent)
// Next 28 bits => 85 - 112 => min center price. BigNumber (20 bits precision, 8 bits exponent)
/// CENTER PRICE SHIFT RELATED THINGS
// First 20 bits => 113 - 132 => % shift (1% = 1000)
// Next 20 bits => 133 - 152 => time to shift that percent, ~12 days max
// Next 33 bits => 153 - 185 => timestamp of when the shift started
// Last 70 bits empty
/// @dev dex id => center price shift
mapping(bytes8 => uint256) internal _centerPriceShift;
/// Range Shift (first 128 bits)
// First 14 bits => 0 - 13 => old upper range percent
// Next 14 bits => 14 - 27 => old lower range percent
// Next 20 bits => 28 - 47 => time to shift in seconds, ~12 days max, shift can last for max ~12 days
// Next 33 bits => 48 - 80 => timestamp of when the shift has started.
// Last 175 bits empty
/// @dev dex id => range shift
mapping(bytes8 => uint256) internal _rangeShift;
// First 7 bits => 0 - 6 => old upper threshold percent
// Next 7 bits => 7 - 13 => old lower threshold percent
// Next 20 bits => 14 - 33 => time to shift in seconds, ~12 days max, shift can last for max ~12 days
// Next 33 bits => 34 - 66 => timestamp of when the shift has started
// Last 189 bits empty
/// @dev dex id => threshold shift
mapping(bytes8 => uint256) internal _thresholdShift;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(
address from,
address to,
uint256 amount
) external returns (bool);
}// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
/// @notice Arithmetic library with operations for fixed-point numbers.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/FixedPointMathLib.sol)
/// @author Inspired by USM (https://github.com/usmfum/USM/blob/master/contracts/WadMath.sol)
library FixedPointMathLib {
/*//////////////////////////////////////////////////////////////
SIMPLIFIED FIXED POINT OPERATIONS
//////////////////////////////////////////////////////////////*/
uint256 internal constant MAX_UINT256 = 2**256 - 1;
uint256 internal constant WAD = 1e18; // The scalar of ETH and most ERC20s.
function mulWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivDown(x, y, WAD); // Equivalent to (x * y) / WAD rounded down.
}
function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivUp(x, y, WAD); // Equivalent to (x * y) / WAD rounded up.
}
function divWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivDown(x, WAD, y); // Equivalent to (x * WAD) / y rounded down.
}
function divWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivUp(x, WAD, y); // Equivalent to (x * WAD) / y rounded up.
}
/*//////////////////////////////////////////////////////////////
LOW LEVEL FIXED POINT OPERATIONS
//////////////////////////////////////////////////////////////*/
function mulDivDown(
uint256 x,
uint256 y,
uint256 denominator
) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))
if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
revert(0, 0)
}
// Divide x * y by the denominator.
z := div(mul(x, y), denominator)
}
}
function mulDivUp(
uint256 x,
uint256 y,
uint256 denominator
) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))
if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
revert(0, 0)
}
// If x * y modulo the denominator is strictly greater than 0,
// 1 is added to round up the division of x * y by the denominator.
z := add(gt(mod(mul(x, y), denominator), 0), div(mul(x, y), denominator))
}
}
function rpow(
uint256 x,
uint256 n,
uint256 scalar
) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
switch x
case 0 {
switch n
case 0 {
// 0 ** 0 = 1
z := scalar
}
default {
// 0 ** n = 0
z := 0
}
}
default {
switch mod(n, 2)
case 0 {
// If n is even, store scalar in z for now.
z := scalar
}
default {
// If n is odd, store x in z for now.
z := x
}
// Shifting right by 1 is like dividing by 2.
let half := shr(1, scalar)
for {
// Shift n right by 1 before looping to halve it.
n := shr(1, n)
} n {
// Shift n right by 1 each iteration to halve it.
n := shr(1, n)
} {
// Revert immediately if x ** 2 would overflow.
// Equivalent to iszero(eq(div(xx, x), x)) here.
if shr(128, x) {
revert(0, 0)
}
// Store x squared.
let xx := mul(x, x)
// Round to the nearest number.
let xxRound := add(xx, half)
// Revert if xx + half overflowed.
if lt(xxRound, xx) {
revert(0, 0)
}
// Set x to scaled xxRound.
x := div(xxRound, scalar)
// If n is even:
if mod(n, 2) {
// Compute z * x.
let zx := mul(z, x)
// If z * x overflowed:
if iszero(eq(div(zx, x), z)) {
// Revert if x is non-zero.
if iszero(iszero(x)) {
revert(0, 0)
}
}
// Round to the nearest number.
let zxRound := add(zx, half)
// Revert if zx + half overflowed.
if lt(zxRound, zx) {
revert(0, 0)
}
// Return properly scaled zxRound.
z := div(zxRound, scalar)
}
}
}
}
}
/*//////////////////////////////////////////////////////////////
GENERAL NUMBER UTILITIES
//////////////////////////////////////////////////////////////*/
function sqrt(uint256 x) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
let y := x // We start y at x, which will help us make our initial estimate.
z := 181 // The "correct" value is 1, but this saves a multiplication later.
// This segment is to get a reasonable initial estimate for the Babylonian method. With a bad
// start, the correct # of bits increases ~linearly each iteration instead of ~quadratically.
// We check y >= 2^(k + 8) but shift right by k bits
// each branch to ensure that if x >= 256, then y >= 256.
if iszero(lt(y, 0x10000000000000000000000000000000000)) {
y := shr(128, y)
z := shl(64, z)
}
if iszero(lt(y, 0x1000000000000000000)) {
y := shr(64, y)
z := shl(32, z)
}
if iszero(lt(y, 0x10000000000)) {
y := shr(32, y)
z := shl(16, z)
}
if iszero(lt(y, 0x1000000)) {
y := shr(16, y)
z := shl(8, z)
}
// Goal was to get z*z*y within a small factor of x. More iterations could
// get y in a tighter range. Currently, we will have y in [256, 256*2^16).
// We ensured y >= 256 so that the relative difference between y and y+1 is small.
// That's not possible if x < 256 but we can just verify those cases exhaustively.
// Now, z*z*y <= x < z*z*(y+1), and y <= 2^(16+8), and either y >= 256, or x < 256.
// Correctness can be checked exhaustively for x < 256, so we assume y >= 256.
// Then z*sqrt(y) is within sqrt(257)/sqrt(256) of sqrt(x), or about 20bps.
// For s in the range [1/256, 256], the estimate f(s) = (181/1024) * (s+1) is in the range
// (1/2.84 * sqrt(s), 2.84 * sqrt(s)), with largest error when s = 1 and when s = 256 or 1/256.
// Since y is in [256, 256*2^16), let a = y/65536, so that a is in [1/256, 256). Then we can estimate
// sqrt(y) using sqrt(65536) * 181/1024 * (a + 1) = 181/4 * (y + 65536)/65536 = 181 * (y + 65536)/2^18.
// There is no overflow risk here since y < 2^136 after the first branch above.
z := shr(18, mul(z, add(y, 65536))) // A mul() is saved from starting z at 181.
// Given the worst case multiplicative error of 2.84 above, 7 iterations should be enough.
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
// If x+1 is a perfect square, the Babylonian method cycles between
// floor(sqrt(x)) and ceil(sqrt(x)). This statement ensures we return floor.
// See: https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division
// Since the ceil is rare, we save gas on the assignment and repeat division in the rare case.
// If you don't care whether the floor or ceil square root is returned, you can remove this statement.
z := sub(z, lt(div(x, z), z))
}
}
function unsafeMod(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// Mod x by y. Note this will return
// 0 instead of reverting if y is zero.
z := mod(x, y)
}
}
function unsafeDiv(uint256 x, uint256 y) internal pure returns (uint256 r) {
/// @solidity memory-safe-assembly
assembly {
// Divide x by y. Note this will return
// 0 instead of reverting if y is zero.
r := div(x, y)
}
}
function unsafeDivUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// Add 1 to x * y if x % y > 0. Note this will
// return 0 instead of reverting if y is zero.
z := add(gt(mod(x, y), 0), div(x, y))
}
}
}{
"optimizer": {
"enabled": true,
"runs": 10000000
},
"evmVersion": "cancun",
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"metadata": {
"useLiteralContent": true
},
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"auth_","type":"address"},{"internalType":"address","name":"liquidity_","type":"address"},{"internalType":"address","name":"deployerContract_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"bytes32","name":"dexId","type":"bytes32"},{"internalType":"uint256","name":"token0AdjustedSupply","type":"uint256"},{"internalType":"uint256","name":"token1AdjustedSupply","type":"uint256"}],"name":"AdjustedSupplyOverflow","type":"error"},{"inputs":[{"internalType":"uint256","name":"amountUnspecified","type":"uint256"},{"internalType":"uint256","name":"amountLimit","type":"uint256"}],"name":"AmountLimitExceeded","type":"error"},{"inputs":[{"internalType":"uint256","name":"amountUnspecified","type":"uint256"},{"internalType":"uint256","name":"amountLimit","type":"uint256"}],"name":"AmountLimitNotMet","type":"error"},{"inputs":[{"internalType":"bytes32","name":"dexId","type":"bytes32"}],"name":"DexNotInitialized","type":"error"},{"inputs":[],"name":"EmptyDexKeysArray","type":"error"},{"inputs":[{"internalType":"uint256","name":"amountUnspecified","type":"uint256"}],"name":"EstimateSwap","type":"error"},{"inputs":[{"internalType":"uint256","name":"adjustedAmount","type":"uint256"},{"internalType":"uint256","name":"imaginaryReserve","type":"uint256"}],"name":"ExcessiveSwapAmount","type":"error"},{"inputs":[{"internalType":"uint256","name":"errorId_","type":"uint256"}],"name":"FluidSafeTransferError","type":"error"},{"inputs":[{"internalType":"uint256","name":"receivedAmount","type":"uint256"},{"internalType":"uint256","name":"requiredAmount","type":"uint256"}],"name":"InsufficientERC20Received","type":"error"},{"inputs":[{"internalType":"uint256","name":"receivedAmount","type":"uint256"},{"internalType":"uint256","name":"requiredAmount","type":"uint256"}],"name":"InsufficientNativeTokenReceived","type":"error"},{"inputs":[{"internalType":"uint256","name":"dexKeysLength","type":"uint256"},{"internalType":"uint256","name":"amountLimitsLength","type":"uint256"}],"name":"InvalidAmountLimitsLength","type":"error"},{"inputs":[],"name":"InvalidMsgValue","type":"error"},{"inputs":[{"internalType":"uint256","name":"pathLength","type":"uint256"},{"internalType":"uint256","name":"dexKeysLength","type":"uint256"}],"name":"InvalidPathLength","type":"error"},{"inputs":[],"name":"InvalidPathTokenOrder","type":"error"},{"inputs":[{"internalType":"uint256","name":"power","type":"uint256"}],"name":"InvalidPower","type":"error"},{"inputs":[{"internalType":"uint256","name":"adjustedAmount","type":"uint256"}],"name":"InvalidSwapAmounts","type":"error"},{"inputs":[{"internalType":"uint256","name":"token0RealReserve","type":"uint256"},{"internalType":"uint256","name":"token1RealReserve","type":"uint256"}],"name":"TokenReservesRatioTooHigh","type":"error"},{"inputs":[{"internalType":"uint256","name":"adjustedAmount","type":"uint256"},{"internalType":"uint256","name":"realReserve","type":"uint256"}],"name":"TokenReservesTooLow","type":"error"},{"inputs":[{"internalType":"address","name":"caller","type":"address"}],"name":"UnauthorizedCaller","type":"error"},{"inputs":[],"name":"ZeroAddress","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"swapData","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"dexVariables","type":"uint256"}],"name":"LogSwap","type":"event"},{"stateMutability":"payable","type":"fallback"},{"inputs":[{"internalType":"bytes32","name":"slot_","type":"bytes32"}],"name":"readFromStorage","outputs":[{"internalType":"uint256","name":"result_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"path_","type":"address[]"},{"components":[{"internalType":"address","name":"token0","type":"address"},{"internalType":"address","name":"token1","type":"address"},{"internalType":"bytes32","name":"salt","type":"bytes32"}],"internalType":"struct DexKey[]","name":"dexKeys_","type":"tuple[]"},{"internalType":"int256","name":"amountSpecified_","type":"int256"},{"internalType":"uint256[]","name":"amountLimits_","type":"uint256[]"},{"components":[{"internalType":"address","name":"to","type":"address"},{"internalType":"bool","name":"isCallback","type":"bool"},{"internalType":"bytes","name":"callbackData","type":"bytes"},{"internalType":"bytes","name":"extraData","type":"bytes"}],"internalType":"struct TransferParams","name":"transferParams_","type":"tuple"}],"name":"swapHop","outputs":[{"internalType":"uint256","name":"amountUnspecified_","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"token0","type":"address"},{"internalType":"address","name":"token1","type":"address"},{"internalType":"bytes32","name":"salt","type":"bytes32"}],"internalType":"struct DexKey","name":"dexKey_","type":"tuple"},{"internalType":"bool","name":"swap0To1_","type":"bool"},{"internalType":"int256","name":"amountSpecified_","type":"int256"},{"internalType":"uint256","name":"amountLimit_","type":"uint256"},{"internalType":"address","name":"to_","type":"address"},{"internalType":"bool","name":"isCallback_","type":"bool"},{"internalType":"bytes","name":"callbackData_","type":"bytes"},{"internalType":"bytes","name":"extraData_","type":"bytes"}],"name":"swapSingle","outputs":[{"internalType":"uint256","name":"amountUnspecified_","type":"uint256"}],"stateMutability":"payable","type":"function"},{"stateMutability":"payable","type":"receive"}]Contract Creation Code
60c060405234801561000f575f5ffd5b506040516140f83803806140f883398101604081905261002e91610074565b6001600160a01b039283165f908152602081905260409020600190559082166080521660a0526100b4565b80516001600160a01b038116811461006f575f5ffd5b919050565b5f5f5f60608486031215610086575f5ffd5b61008f84610059565b925061009d60208501610059565b91506100ab60408501610059565b90509250925092565b60805160a05161401c6100dc5f395f818161247901526131c601525f6101ee015261401c5ff3fe608060405260043610610037575f3560e01c80637fc9d4ad146100f7578063a3c779fd1461011c578063b5c736e41461012f5761003e565b3661003e57005b5f36606061004a61014d565b335f9081526020819052604090205460011480159061008657503361006d61019e565b73ffffffffffffffffffffffffffffffffffffffff1614155b156100c4576040517fd86ad9cf0000000000000000000000000000000000000000000000000000000081523360048201526024015b60405180910390fd5b5f806100d2848601866137f8565b915091506100e08282610271565b925050506100ec6102b3565b915050805190602001f35b61010a61010536600461395f565b6102d8565b60405190815260200160405180910390f35b61010a61012a366004613a85565b610634565b34801561013a575f5ffd5b5061010a610149366004613b88565b5490565b7fb9cde754d19acfff2b3ccabc66f256d3563a0bc5805da4205f01a9bda38a2df75c15610178575f5ffd5b60017fb9cde754d19acfff2b3ccabc66f256d3563a0bc5805da4205f01a9bda38a2df75d565b6040517fb5c736e40000000000000000000000000000000000000000000000000000000081527fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d610360048201525f907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063b5c736e490602401602060405180830381865afa158015610248573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061026c9190613b9f565b905090565b60605f5f835160208501865af43d6040519250601f19601f6020830101168301604052808352805f602085013e816102ab57805f5f3e805ffd5b505092915050565b5f7fb9cde754d19acfff2b3ccabc66f256d3563a0bc5805da4205f01a9bda38a2df75d565b5f6102e161014d565b5f8913156104a6576102f48b8b8b610ff5565b90508781101561033a576040517f552ac31300000000000000000000000000000000000000000000000000000000815260048101829052602481018990526044016100bb565b5f82900361039c57891561037b5761037661035860208d018d613bb6565b8a8d602001602081019061036c9190613bb6565b848b8b8b8b611794565b61061e565b61037661038e60408d0160208e01613bb6565b8a61036c60208f018f613bb6565b60408051808201909152600d81527f455354494d4154455f53574150000000000000000000000000000000000000006020909101527f9fbe8fb0ae8f68c4e90aaac8dee895c5ba9596470c68a601101c9d65a103a9436103fc8385613bd8565b03610436576040517f5fea03c9000000000000000000000000000000000000000000000000000000008152600481018290526024016100bb565b61037660018c8c8c85888860405160200161045696959493929190613cb2565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152908290526104929291602001613ce6565b604051602081830303815290604052611c54565b6104b98b8b6104b48c613d6f565b611cdb565b9050878111156104ff576040517f97121c2100000000000000000000000000000000000000000000000000000000815260048101829052602481018990526044016100bb565b5f8290036105645789156105435761037661051d60208d018d613bb6565b828d60200160208101906105319190613bb6565b61053a8d613d6f565b8b8b8b8b611794565b61037661055660408d0160208e01613bb6565b8261053160208f018f613bb6565b60408051808201909152600d81527f455354494d4154455f53574150000000000000000000000000000000000000006020909101527f9fbe8fb0ae8f68c4e90aaac8dee895c5ba9596470c68a601101c9d65a103a9436105c48385613bd8565b036105fe576040517f5fea03c9000000000000000000000000000000000000000000000000000000008152600481018290526024016100bb565b61061e60018c8c8c85888860405160200161045696959493929190613cb2565b6106266102b3565b9a9950505050505050505050565b5f61063d61014d565b5f869003610677576040517fd15d5eeb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8561068360018a613da5565b146106c4576040517f88b3170e00000000000000000000000000000000000000000000000000000000815260048101899052602481018790526044016100bb565b828614610707576040517fb932f13700000000000000000000000000000000000000000000000000000000815260048101849052602481018790526044016100bb565b5f851315610b865750835f5b86811015610a15575f88888381811061072e5761072e613db8565b6107449260206060909202019081019150613bb6565b73ffffffffffffffffffffffffffffffffffffffff168b8b8481811061076c5761076c613db8565b90506020020160208101906107819190613bb6565b73ffffffffffffffffffffffffffffffffffffffff1614801561082157508888838181106107b1576107b1613db8565b90506060020160200160208101906107c99190613bb6565b73ffffffffffffffffffffffffffffffffffffffff168b8b846001018181106107f4576107f4613db8565b90506020020160208101906108099190613bb6565b73ffffffffffffffffffffffffffffffffffffffff16145b1561082e57506001610971565b88888381811061084057610840613db8565b90506060020160200160208101906108589190613bb6565b73ffffffffffffffffffffffffffffffffffffffff168b8b8481811061088057610880613db8565b90506020020160208101906108959190613bb6565b73ffffffffffffffffffffffffffffffffffffffff1614801561093357508888838181106108c5576108c5613db8565b6108db9260206060909202019081019150613bb6565b73ffffffffffffffffffffffffffffffffffffffff168b8b8460010181811061090657610906613db8565b905060200201602081019061091b9190613bb6565b73ffffffffffffffffffffffffffffffffffffffff16145b1561093f57505f610971565b6040517f0a33bc9500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61099389898481811061098657610986613db8565b9050606002018285610ff5565b92508585838181106109a7576109a7613db8565b90506020020135831015610a0c57828686848181106109c8576109c8613db8565b905060200201356040517f552ac3130000000000000000000000000000000000000000000000000000000081526004016100bb929190918252602082015260400190565b50600101610713565b50610a236060830183613de5565b90505f03610ab257610aad89895f818110610a4057610a40613db8565b9050602002016020810190610a559190613bb6565b868b8b8a818110610a6857610a68613db8565b9050602002016020810190610a7d9190613bb6565b84610a8b6020880188613bb6565b610a9b6040890160208a01613e46565b610aa860408a018a613de5565b611794565b610fe1565b60408051808201909152600d81527f455354494d4154455f53574150000000000000000000000000000000000000006020909101527f9fbe8fb0ae8f68c4e90aaac8dee895c5ba9596470c68a601101c9d65a103a943610b156060840184613de5565b610b1e91613bd8565b03610b58576040517f5fea03c9000000000000000000000000000000000000000000000000000000008152600481018290526024016100bb565b610aad5f8a8a8a8a8a87610b6f60608b018b613de5565b604051602001610456989796959493929190613e5f565b610b8f85613d6f565b9050855b8015610ea6575f888860018403818110610baf57610baf613db8565b610bc59260206060909202019081019150613bb6565b73ffffffffffffffffffffffffffffffffffffffff168b8b60018503818110610bf057610bf0613db8565b9050602002016020810190610c059190613bb6565b73ffffffffffffffffffffffffffffffffffffffff16148015610ca55750888860018403818110610c3857610c38613db8565b9050606002016020016020810190610c509190613bb6565b73ffffffffffffffffffffffffffffffffffffffff168b8b84818110610c7857610c78613db8565b9050602002016020810190610c8d9190613bb6565b73ffffffffffffffffffffffffffffffffffffffff16145b15610cb257506001610dc5565b888860018403818110610cc757610cc7613db8565b9050606002016020016020810190610cdf9190613bb6565b73ffffffffffffffffffffffffffffffffffffffff168b8b60018503818110610d0a57610d0a613db8565b9050602002016020810190610d1f9190613bb6565b73ffffffffffffffffffffffffffffffffffffffff16148015610dbd5750888860018403818110610d5257610d52613db8565b610d689260206060909202019081019150613bb6565b73ffffffffffffffffffffffffffffffffffffffff168b8b84818110610d9057610d90613db8565b9050602002016020810190610da59190613bb6565b73ffffffffffffffffffffffffffffffffffffffff16145b1561093f57505f5b610df18989610dd5600186613da5565b818110610de457610de4613db8565b9050606002018285611cdb565b92508585610e00600185613da5565b818110610e0f57610e0f613db8565b90506020020135831115610e7e57828686610e2b600186613da5565b818110610e3a57610e3a613db8565b905060200201356040517f97121c210000000000000000000000000000000000000000000000000000000081526004016100bb929190918252602082015260400190565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01610b93565b50610eb46060830183613de5565b90505f03610f2457610aad89895f818110610ed157610ed1613db8565b9050602002016020810190610ee69190613bb6565b828b8b8a818110610ef957610ef9613db8565b9050602002016020810190610f0e9190613bb6565b610f1789613d6f565b610a8b6020880188613bb6565b60408051808201909152600d81527f455354494d4154455f53574150000000000000000000000000000000000000006020909101527f9fbe8fb0ae8f68c4e90aaac8dee895c5ba9596470c68a601101c9d65a103a943610f876060840184613de5565b610f9091613bd8565b03610fca576040517f5fea03c9000000000000000000000000000000000000000000000000000000008152600481018290526024016100bb565b610fe15f8a8a8a8a8a87610b6f60608b018b613de5565b610fe96102b3565b98975050505050505050565b5f5f846040516020016110089190613f1d565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815291815281516020928301207fffffffffffffffff00000000000000000000000000000000000000000000000081165f908152600290935290822054909250908190036110cf576040517f722948110000000000000000000000000000000000000000000000000000000081527fffffffffffffffff000000000000000000000000000000000000000000000000831660048201526024016100bb565b608881901c670fffffffffffffff1660c482901c5f80806110f38b87898888612435565b92509250925089156112b857607e86901c601f1660098111156111325761111c60098203612844565b8a8161112a5761112a613f2b565b049950611143565b61113e81600903612844565b8a0299505b6127108a108061115a5750670fffffffffffffff8a115b15611194576040517f17d5c4d2000000000000000000000000000000000000000000000000000000008152600481018b90526024016100bb565b600283048a11156111db576040517ffecbb38c000000000000000000000000000000000000000000000000000000008152600481018b9052602481018490526044016100bb565b620f4240611fff88168b0204808b03848101908402816111fd576111fd613f2b565b0499506064600d89901c607f168202048b038701965089861015611257576040517f473f499f000000000000000000000000000000000000000000000000000000008152600481018b9052602481018790526044016100bb565b94899003946c7e37be2022c0914b2680000000878602048610156112b1576040517fb499466c00000000000000000000000000000000000000000000000000000000815260048101889052602481018790526044016100bb565b505061147d565b608386901c601f1660098111156112eb576112d560098203612844565b8a816112e3576112e3613f2b565b0499506112fc565b6112f781600903612844565b8a0299505b6127108a10806113135750670fffffffffffffff8a115b1561134d576040517f17d5c4d2000000000000000000000000000000000000000000000000000000008152600481018b90526024016100bb565b600282048a1115611394576040517ffecbb38c000000000000000000000000000000000000000000000000000000008152600481018b9052602481018390526044016100bb565b620f4240611fff88168b0204808b03838101908502816113b6576113b6613f2b565b0499506064600d89901c607f168202048b038601955089871015611410576040517f473f499f000000000000000000000000000000000000000000000000000000008152600481018b9052602481018890526044016100bb565b898703965061271085026b033b2e3c9fd0803ce800000087028161143657611436613f2b565b0487101561147a576040517fb499466c00000000000000000000000000000000000000000000000000000000815260048101889052602481018790526044016100bb565b50505b601486901c60031680156114ee575f8b6114b8578984036b033b2e3c9fd0803ce80000008c850102816114b2576114b2613f2b565b046114db565b8a84016b033b2e3c9fd0803ce80000008b850302816114d9576114d9613f2b565b045b90506114ea888a848489612927565b9150505b600181118061150457506001601688901c166001145b15611562577fffffffffffffffff00000000000000000000000000000000000000000000000088165f90815260036020526040902080547ffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0000000016421790555b50670fffffffffffffff8511806115805750670fffffffffffffff84115b156115e9576040517f9e0db8780000000000000000000000000000000000000000000000000000000081527fffffffffffffffff0000000000000000000000000000000000000000000000008816600482015260248101869052604481018590526064016100bb565b7fffffffffffffffff00000000000000000000000000000000000000000000000087165f908152600260205260409020805470ffffffffffffffffffffffffffffffffff16608887901b1760c486901b1790819055955089156116f0576040805168010000000000000000607d8b901b60418d901b60c08c901c1717178152602081018890527ffbce846c23a724e6e61161894819ec46c90a8d3dd96e90e7342c6ef49ffb539c910160405180910390a1608386901c601f1660098111156116cf576116be6116b9600983613da5565b612844565b6116c8908a613f58565b98506116ea565b6116dd6116b9826009613da5565b6116e7908a613f6f565b98505b50611786565b60408051607d8a901b60418c901b60c08b901c17178152602081018890527ffbce846c23a724e6e61161894819ec46c90a8d3dd96e90e7342c6ef49ffb539c910160405180910390a1607e86901c601f166009811115611769576117586116b9600983613da5565b611762908a613f58565b9850611784565b6117776116b9826009613da5565b611781908a613f6f565b98505b505b505050505050509392505050565b73ffffffffffffffffffffffffffffffffffffffff84166117b3573393505b7fffffffffffffffffffffffff111111111111111111111111111111111111111273ffffffffffffffffffffffffffffffffffffffff8716016117ff576117fa8486612b3f565b61180a565b61180a868587612b88565b7fffffffffffffffffffffffff111111111111111111111111111111111111111273ffffffffffffffffffffffffffffffffffffffff89160161198b57828015611852575034155b15611928576040517fb6a545480000000000000000000000000000000000000000000000000000000081524790339063b6a545489061189b908c908c9088908890600401613fa7565b5f604051808303815f87803b1580156118b2575f5ffd5b505af11580156118c4573d5f5f3e3d5ffd5b505050508781476118d59190613da5565b1015611922576118e58147613da5565b6040517fbdd289ca0000000000000000000000000000000000000000000000000000000081526004810191909152602481018990526044016100bb565b50611c4a565b8634101561196b576040517fbdd289ca000000000000000000000000000000000000000000000000000000008152346004820152602481018890526044016100bb565b8634111561198657611986336119818934613da5565b612b3f565b611c4a565b34156119c3576040517f1841b4e100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8215611c3e576040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201525f9073ffffffffffffffffffffffffffffffffffffffff8a16906370a0823190602401602060405180830381865afa158015611a33573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611a579190613b9f565b6040517fb6a54548000000000000000000000000000000000000000000000000000000008152909150339063b6a5454890611a9c908c908c9088908890600401613fa7565b5f604051808303815f87803b158015611ab3575f5ffd5b505af1158015611ac5573d5f5f3e3d5ffd5b50506040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201528a925083915073ffffffffffffffffffffffffffffffffffffffff8c16906370a0823190602401602060405180830381865afa158015611b35573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611b599190613b9f565b611b639190613da5565b1015611922576040517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152819073ffffffffffffffffffffffffffffffffffffffff8b16906370a0823190602401602060405180830381865afa158015611bd3573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611bf79190613b9f565b611c019190613da5565b6040517faf35ea9e0000000000000000000000000000000000000000000000000000000081526004810191909152602481018990526044016100bb565b611c4a8833308a612c2e565b5050505050505050565b5f611c7d7f7e8134afb5ed35d36cb65e24b9a4712a52bb77d952806c1acf50970d2107797f5490565b905073ffffffffffffffffffffffffffffffffffffffff8116611ccc576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611cd68183610271565b505050565b5f5f84604051602001611cee9190613f1d565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815291815281516020928301207fffffffffffffffff00000000000000000000000000000000000000000000000081165f90815260029093529082205490925090819003611db5576040517f722948110000000000000000000000000000000000000000000000000000000081527fffffffffffffffff000000000000000000000000000000000000000000000000831660048201526024016100bb565b608881901c670fffffffffffffff1660c482901c5f8080611dd98b87898888612435565b9250925092508915611fb557608386901c601f166009811115611e1857611e0260098203612844565b8a81611e1057611e10613f2b565b049950611e29565b611e2481600903612844565b8a0299505b6127108a1080611e405750670fffffffffffffff8a115b15611e7a576040517f17d5c4d2000000000000000000000000000000000000000000000000000000008152600481018b90526024016100bb565b600282048a1115611ec1576040517ffecbb38c000000000000000000000000000000000000000000000000000000008152600481018b9052602481018390526044016100bb565b898203838b0281611ed457611ed4613f2b565b0498505f89611fff8916620f424090810390820281611ef557611ef5613f2b565b0403998a019990506064600d89901c607f168202048a03870196508a861015611f54576040517f473f499f000000000000000000000000000000000000000000000000000000008152600481018c9052602481018790526044016100bb565b948a9003946c7e37be2022c0914b268000000087860204861015611fae576040517fb499466c00000000000000000000000000000000000000000000000000000000815260048101889052602481018790526044016100bb565b5050612191565b607e86901c601f166009811115611fe857611fd260098203612844565b8a81611fe057611fe0613f2b565b049950611ff9565b611ff481600903612844565b8a0299505b6127108a10806120105750670fffffffffffffff8a115b1561204a576040517f17d5c4d2000000000000000000000000000000000000000000000000000000008152600481018b90526024016100bb565b600283048a1115612091576040517ffecbb38c000000000000000000000000000000000000000000000000000000008152600481018b9052602481018490526044016100bb565b898303828b02816120a4576120a4613f2b565b0498505f89611fff8916620f4240908103908202816120c5576120c5613f2b565b0403998a019990506064600d89901c607f168202048a03860195508a871015612124576040517f473f499f000000000000000000000000000000000000000000000000000000008152600481018c9052602481018890526044016100bb565b8a8703965061271085026b033b2e3c9fd0803ce800000087028161214a5761214a613f2b565b0487101561218e576040517fb499466c00000000000000000000000000000000000000000000000000000000815260048101889052602481018790526044016100bb565b50505b601486901c6003168015612202575f8b6121cc578a84036b033b2e3c9fd0803ce80000008b850102816121c6576121c6613f2b565b046121ef565b8984016b033b2e3c9fd0803ce80000008c850302816121ed576121ed613f2b565b045b90506121fe888a848489612927565b9150505b600181118061221857506001601688901c166001145b15612276577fffffffffffffffff00000000000000000000000000000000000000000000000088165f90815260036020526040902080547ffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0000000016421790555b50670fffffffffffffff8511806122945750670fffffffffffffff84115b156122fd576040517f9e0db8780000000000000000000000000000000000000000000000000000000081527fffffffffffffffff0000000000000000000000000000000000000000000000008816600482015260248101869052604481018590526064016100bb565b7fffffffffffffffff00000000000000000000000000000000000000000000000087165f908152600260205260409020805470ffffffffffffffffffffffffffffffffff16608887901b1760c486901b1790819055955089156123cd576040805168010000000000000000607d8c901b60418c901b60c08c901c1717178152602081018890527ffbce846c23a724e6e61161894819ec46c90a8d3dd96e90e7342c6ef49ffb539c910160405180910390a1607e86901c601f1660098111156116cf576116be6116b9600983613da5565b60408051607d8b901b60418b901b60c08b901c17178152602081018890527ffbce846c23a724e6e61161894819ec46c90a8d3dd96e90e7342c6ef49ffb539c910160405180910390a1608386901c601f166009811115611769576117586116b9600983613da5565b5f5f5f6001601688901c165f03612570576207ffff603f88901c169250825f036124745763ffffffff601f88901c1660ff601789901c161b925061257e565b61249e7f000000000000000000000000000000000000000000000000000000000000000084612cf1565b73ffffffffffffffffffffffffffffffffffffffff166302c59a686124c660208b018b613bb6565b6124d660408c0160208d01613bb6565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b16815273ffffffffffffffffffffffffffffffffffffffff9283166004820152911660248201526044016020604051808303815f875af1158015612545573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906125699190613b9f565b925061257e565b61257b88888861315a565b92505b613fff605388901c811690606189901c16600160528a901c811690036125af576125a982828a61344e565b90925090505b5f5f83612710036127108802816125c8576125c8613f2b565b04915050612710828103870204600360148c901c165f6001821115612751578160020361266557507fffffffffffffffff0000000000000000000000000000000000000000000000008b165f90815260036020526040902054602181901c62ffffff166401ffffffff821642038181101561265a5781818c8803028161265057612650613f2b565b048b019a5061265e565b859a505b50506126df565b816003036126df57507fffffffffffffffff0000000000000000000000000000000000000000000000008b165f90815260036020526040902054602181901c62ffffff166401ffffffff82164203818110156126d8578181868d0302816126ce576126ce613f2b565b048b039a506126dc565b849a505b50505b801561275157620fffff604182901c1660ff603983901c161b91508189111561270a5781985061272b565b620fffff605d82901c1660ff605583901c161b91508189101561272b578198505b85612710036127108a028161274257612742613f2b565b0493506127108581038a020492505b6f4b3b4ca85a86c47a098a22400000000084101561277b5761277483850261354d565b91506127a7565b61279a670de0b6b3a76400008404670de0b6b3a764000086040261354d565b670de0b6b3a76400000291505b6b033b2e3c9fd0803ce80000008210156127d1576127c782858d8d6135ff565b909850965061282e565b61282982760a70c3c40a64e6c51999090b65f67d9240000000000000816127fa576127fa613f2b565b0484760a70c3c40a64e6c51999090b65f67d92400000000000008161282157612821613f2b565b048c8e6135ff565b985096505b50969c959098019a505050930195509350505050565b5f8160030361285657506103e8919050565b816009036128695750633b9aca00919050565b816001036128795750600a919050565b815f0361288857506001919050565b8160020361289857506064919050565b816004036128a95750612710919050565b816005036128bb5750620186a0919050565b816006036128cd5750620f4240919050565b816007036128df575062989680919050565b816008036128f257506305f5e100919050565b6040517f42cfde25000000000000000000000000000000000000000000000000000000008152600481018390526024016100bb565b5f613fff605387901c811690606188901c166001605289901c811690036129595761295382828961344e565b90925090505b816127100361271085028161297057612970613f2b565b0491506127108181038502049050607f607089901c81169060778a901c166001606f8b901c811690036129ae576129a882828b613678565b90925090505b6064918203868503028290048601919081038387030204850381871115612a465787600214612a41575050507fffffffffffffffff00000000000000000000000000000000000000000000000086165f90815260026020819052604090912080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcfffff16622000001790559150612b369050565b612b2e565b80871015612abe5787600314612a41575050507fffffffffffffffff00000000000000000000000000000000000000000000000086165f90815260026020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcfffff16623000001790555060039050612b36565b87600114612b2e575050507fffffffffffffffff00000000000000000000000000000000000000000000000086165f90815260026020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcfffff16621000001790555060019050612b36565b879450505050505b95945050505050565b5f5f5f5f5f8587614e20f1905080611cd6576040517fdee51a8a0000000000000000000000000000000000000000000000000000000081526201155a60048201526024016100bb565b5f6040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8416600482015282602482015260205f6044835f895af13d15601f3d1160015f511416171691505080612c28576040517fdee51a8a0000000000000000000000000000000000000000000000000000000081526201155a60048201526024016100bb565b50505050565b5f6040517f23b872dd00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8516600482015273ffffffffffffffffffffffffffffffffffffffff8416602482015282604482015260205f6064835f8a5af13d15601f3d1160015f511416171691505080612cea576040517fdee51a8a0000000000000000000000000000000000000000000000000000000081526201155960048201526024016100bb565b5050505050565b5f6060825f03612d04575f915050613154565b607f8311612dcd576040517fd60000000000000000000000000000000000000000000000000000000000000060208201527f940000000000000000000000000000000000000000000000000000000000000060218201527fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606086901b16602282015260f884901b7fff000000000000000000000000000000000000000000000000000000000000001660368201526037015b6040516020818303038152906040529050613149565b60ff8311612eaa576040517fd70000000000000000000000000000000000000000000000000000000000000060208201527f940000000000000000000000000000000000000000000000000000000000000060218201527fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606086901b1660228201527f8100000000000000000000000000000000000000000000000000000000000000603682015260f884901b7fff00000000000000000000000000000000000000000000000000000000000000166037820152603801612db7565b61ffff8311612f88576040517fd80000000000000000000000000000000000000000000000000000000000000060208201527f940000000000000000000000000000000000000000000000000000000000000060218201527fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606086901b1660228201527f820000000000000000000000000000000000000000000000000000000000000060368201527fffff00000000000000000000000000000000000000000000000000000000000060f085901b166037820152603901612db7565b62ffffff8311613067576040517fd90000000000000000000000000000000000000000000000000000000000000060208201527f940000000000000000000000000000000000000000000000000000000000000060218201527fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606086901b1660228201527f830000000000000000000000000000000000000000000000000000000000000060368201527fffffff000000000000000000000000000000000000000000000000000000000060e885901b166037820152603a01612db7565b6040517fda0000000000000000000000000000000000000000000000000000000000000060208201527f940000000000000000000000000000000000000000000000000000000000000060218201527fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606086901b1660228201527f840000000000000000000000000000000000000000000000000000000000000060368201527fffffffff0000000000000000000000000000000000000000000000000000000060e085901b166037820152603b0160405160208183030381529060405290505b805160209091012090505b92915050565b7fffffffffffffffff00000000000000000000000000000000000000000000000081165f90815260036020526040812054601f84901c63ffffffff16601785901c60ff161b906401ffffffff609982901c81169082168181116131bd57816131bf565b805b90506131f47f00000000000000000000000000000000000000000000000000000000000000006207ffff603f8a901c16612cf1565b73ffffffffffffffffffffffffffffffffffffffff166302c59a6861321c60208b018b613bb6565b61322c60408c0160208d01613bb6565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b16815273ffffffffffffffffffffffffffffffffffffffff9283166004820152911660248201526044016020604051808303815f875af115801561329b573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906132bf9190613b9f565b94505f620f4240620fffff608586901c1602824203620fffff607187901c16870202816132ee576132ee613f2b565b0490508486111561339857938401938486111561330d57849550613442565b7fffffffffffffffff00000000000000000000000000000000000000000000000087165f90815260036020908152604080832080547ffffffffffffffffffc000000000000000001ffffffffffffffffffffffffffff1690556002909152902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbfffff169055613442565b8085116133a5575f6133a9565b8085035b9450848610156133bb57849550613442565b7fffffffffffffffff00000000000000000000000000000000000000000000000087165f90815260036020908152604080832080547ffffffffffffffffffc000000000000000001ffffffffffffffffffffffffffff1690556002909152902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbfffff1690555b50505050509392505050565b7fffffffffffffffff00000000000000000000000000000000000000000000000081165f908152600460205260408120548190601c81901c620fffff16603082901c6401ffffffff168342828401101561351357505050507fffffffffffffffff00000000000000000000000000000000000000000000000083165f9081526004602090815260408083208390556002909152902080547ffffffffffffffffffffffffffffffffffffffffffffbffffffffffffffffffff1690555083905082613545565b504281900361352889613fff86168386613760565b61353c89613fff600e88901c168487613760565b95509550505050505b935093915050565b60b5817101000000000000000000000000000000000081106135745760409190911b9060801c5b690100000000000000000081106135905760209190911b9060401c5b6501000000000081106135a85760109190911b9060201c5b630100000081106135be5760089190911b9060101c5b62010000010260121c80820401600190811c80830401811c80830401811c80830401811c80830401811c80830401811c80830401901c908190048111900390565b5f8085850381600282028689026b033b2e3c9fd0803ce80000008702018161362957613629613f2b565b049050613658818202836b033b2e3c9fd0803ce8000000888a02028161365157613651613f2b565b040161354d565b01976b033b2e3c9fd0803ce8000000978902979097049695505050505050565b7fffffffffffffffff00000000000000000000000000000000000000000000000081165f908152600560205260408120548190600e81901c620fffff16602282901c6401ffffffff168342828401101561373d57505050507fffffffffffffffff00000000000000000000000000000000000000000000000083165f9081526005602090815260408083208390556002909152902080547fffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffff1690555083905082613545565b504281900361375189607f86168386613760565b61353c89607f600788901c1684875b5f83851115613786578183858703028161377c5761377c613f2b565b048401905061379f565b8183868603028161379957613799613f2b565b04840390505b949350505050565b73ffffffffffffffffffffffffffffffffffffffff811681146137c8575f5ffd5b50565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b5f5f60408385031215613809575f5ffd5b8235613814816137a7565b9150602083013567ffffffffffffffff81111561382f575f5ffd5b8301601f8101851361383f575f5ffd5b803567ffffffffffffffff811115613859576138596137cb565b6040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0603f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8501160116810181811067ffffffffffffffff821117156138c5576138c56137cb565b6040528181528282016020018710156138dc575f5ffd5b816020840160208301375f602083830101528093505050509250929050565b8035801515811461390a575f5ffd5b919050565b803561390a816137a7565b5f5f83601f84011261392a575f5ffd5b50813567ffffffffffffffff811115613941575f5ffd5b602083019150836020828501011115613958575f5ffd5b9250929050565b5f5f5f5f5f5f5f5f5f5f8a8c0361014081121561397a575f5ffd5b6060811215613987575f5ffd5b508a995061399760608c016138fb565b985060808b0135975060a08b013596506139b360c08c0161390f565b95506139c160e08c016138fb565b94506101008b013567ffffffffffffffff8111156139dd575f5ffd5b6139e98d828e0161391a565b9095509350506101208b013567ffffffffffffffff811115613a09575f5ffd5b613a158d828e0161391a565b915080935050809150509295989b9194979a5092959850565b5f5f83601f840112613a3e575f5ffd5b50813567ffffffffffffffff811115613a55575f5ffd5b6020830191508360208260051b8501011115613958575f5ffd5b5f60808284031215613a7f575f5ffd5b50919050565b5f5f5f5f5f5f5f5f60a0898b031215613a9c575f5ffd5b883567ffffffffffffffff811115613ab2575f5ffd5b613abe8b828c01613a2e565b909950975050602089013567ffffffffffffffff811115613add575f5ffd5b8901601f81018b13613aed575f5ffd5b803567ffffffffffffffff811115613b03575f5ffd5b8b6020606083028401011115613b17575f5ffd5b6020919091019650945060408901359350606089013567ffffffffffffffff811115613b41575f5ffd5b613b4d8b828c01613a2e565b909450925050608089013567ffffffffffffffff811115613b6c575f5ffd5b613b788b828c01613a6f565b9150509295985092959890939650565b5f60208284031215613b98575f5ffd5b5035919050565b5f60208284031215613baf575f5ffd5b5051919050565b5f60208284031215613bc6575f5ffd5b8135613bd1816137a7565b9392505050565b80356020831015613154577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff602084900360031b1b1692915050565b8035613c1f816137a7565b73ffffffffffffffffffffffffffffffffffffffff1682526020810135613c45816137a7565b73ffffffffffffffffffffffffffffffffffffffff166020830152604090810135910152565b81835281816020850137505f602082840101525f60207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b613cbc8188613c14565b85151560608201528460808201528360a082015260e060c08201525f610fe960e083018486613c6b565b8215158152604060208201525f82518060408401528060208501606085015e5f6060828501015260607fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8301168401019150509392505050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f7f80000000000000000000000000000000000000000000000000000000000000008203613d9f57613d9f613d42565b505f0390565b8181038181111561315457613154613d42565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5f5f83357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112613e18575f5ffd5b83018035915067ffffffffffffffff821115613e32575f5ffd5b602001915036819003821315613958575f5ffd5b5f60208284031215613e56575f5ffd5b613bd1826138fb565b60a080825281018890525f8960c08301825b8b811015613eae578235613e84816137a7565b73ffffffffffffffffffffffffffffffffffffffff16825260209283019290910190600101613e71565b50838103602080860191909152898252019050885f805b8a811015613eea57613ed78484613c14565b6060938401939290920191600101613ec5565b50505060408301879052606083018690528281036080840152613f0e818587613c6b565b9b9a5050505050505050505050565b606081016131548284613c14565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b808202811582820484141761315457613154613d42565b5f82613fa2577f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b500490565b73ffffffffffffffffffffffffffffffffffffffff85168152836020820152606060408201525f613fdc606083018486613c6b565b969550505050505056fea264697066735822122048314b8dec15e796decf36eb636ff0c09330021781b58dfce741aa70a3f264ab64736f6c634300081d00330000000000000000000000008a5b57d047d284a2a6ec774a8bca2beab2a6c95500000000000000000000000052aa899454998be5b000ad077a46bbe360f4e4970000000000000000000000004ec7b668baf70d4a4b0fc7941a7708a07b6d45be
Deployed Bytecode
0x608060405260043610610037575f3560e01c80637fc9d4ad146100f7578063a3c779fd1461011c578063b5c736e41461012f5761003e565b3661003e57005b5f36606061004a61014d565b335f9081526020819052604090205460011480159061008657503361006d61019e565b73ffffffffffffffffffffffffffffffffffffffff1614155b156100c4576040517fd86ad9cf0000000000000000000000000000000000000000000000000000000081523360048201526024015b60405180910390fd5b5f806100d2848601866137f8565b915091506100e08282610271565b925050506100ec6102b3565b915050805190602001f35b61010a61010536600461395f565b6102d8565b60405190815260200160405180910390f35b61010a61012a366004613a85565b610634565b34801561013a575f5ffd5b5061010a610149366004613b88565b5490565b7fb9cde754d19acfff2b3ccabc66f256d3563a0bc5805da4205f01a9bda38a2df75c15610178575f5ffd5b60017fb9cde754d19acfff2b3ccabc66f256d3563a0bc5805da4205f01a9bda38a2df75d565b6040517fb5c736e40000000000000000000000000000000000000000000000000000000081527fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d610360048201525f907f00000000000000000000000052aa899454998be5b000ad077a46bbe360f4e49773ffffffffffffffffffffffffffffffffffffffff169063b5c736e490602401602060405180830381865afa158015610248573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061026c9190613b9f565b905090565b60605f5f835160208501865af43d6040519250601f19601f6020830101168301604052808352805f602085013e816102ab57805f5f3e805ffd5b505092915050565b5f7fb9cde754d19acfff2b3ccabc66f256d3563a0bc5805da4205f01a9bda38a2df75d565b5f6102e161014d565b5f8913156104a6576102f48b8b8b610ff5565b90508781101561033a576040517f552ac31300000000000000000000000000000000000000000000000000000000815260048101829052602481018990526044016100bb565b5f82900361039c57891561037b5761037661035860208d018d613bb6565b8a8d602001602081019061036c9190613bb6565b848b8b8b8b611794565b61061e565b61037661038e60408d0160208e01613bb6565b8a61036c60208f018f613bb6565b60408051808201909152600d81527f455354494d4154455f53574150000000000000000000000000000000000000006020909101527f9fbe8fb0ae8f68c4e90aaac8dee895c5ba9596470c68a601101c9d65a103a9436103fc8385613bd8565b03610436576040517f5fea03c9000000000000000000000000000000000000000000000000000000008152600481018290526024016100bb565b61037660018c8c8c85888860405160200161045696959493929190613cb2565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152908290526104929291602001613ce6565b604051602081830303815290604052611c54565b6104b98b8b6104b48c613d6f565b611cdb565b9050878111156104ff576040517f97121c2100000000000000000000000000000000000000000000000000000000815260048101829052602481018990526044016100bb565b5f8290036105645789156105435761037661051d60208d018d613bb6565b828d60200160208101906105319190613bb6565b61053a8d613d6f565b8b8b8b8b611794565b61037661055660408d0160208e01613bb6565b8261053160208f018f613bb6565b60408051808201909152600d81527f455354494d4154455f53574150000000000000000000000000000000000000006020909101527f9fbe8fb0ae8f68c4e90aaac8dee895c5ba9596470c68a601101c9d65a103a9436105c48385613bd8565b036105fe576040517f5fea03c9000000000000000000000000000000000000000000000000000000008152600481018290526024016100bb565b61061e60018c8c8c85888860405160200161045696959493929190613cb2565b6106266102b3565b9a9950505050505050505050565b5f61063d61014d565b5f869003610677576040517fd15d5eeb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8561068360018a613da5565b146106c4576040517f88b3170e00000000000000000000000000000000000000000000000000000000815260048101899052602481018790526044016100bb565b828614610707576040517fb932f13700000000000000000000000000000000000000000000000000000000815260048101849052602481018790526044016100bb565b5f851315610b865750835f5b86811015610a15575f88888381811061072e5761072e613db8565b6107449260206060909202019081019150613bb6565b73ffffffffffffffffffffffffffffffffffffffff168b8b8481811061076c5761076c613db8565b90506020020160208101906107819190613bb6565b73ffffffffffffffffffffffffffffffffffffffff1614801561082157508888838181106107b1576107b1613db8565b90506060020160200160208101906107c99190613bb6565b73ffffffffffffffffffffffffffffffffffffffff168b8b846001018181106107f4576107f4613db8565b90506020020160208101906108099190613bb6565b73ffffffffffffffffffffffffffffffffffffffff16145b1561082e57506001610971565b88888381811061084057610840613db8565b90506060020160200160208101906108589190613bb6565b73ffffffffffffffffffffffffffffffffffffffff168b8b8481811061088057610880613db8565b90506020020160208101906108959190613bb6565b73ffffffffffffffffffffffffffffffffffffffff1614801561093357508888838181106108c5576108c5613db8565b6108db9260206060909202019081019150613bb6565b73ffffffffffffffffffffffffffffffffffffffff168b8b8460010181811061090657610906613db8565b905060200201602081019061091b9190613bb6565b73ffffffffffffffffffffffffffffffffffffffff16145b1561093f57505f610971565b6040517f0a33bc9500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61099389898481811061098657610986613db8565b9050606002018285610ff5565b92508585838181106109a7576109a7613db8565b90506020020135831015610a0c57828686848181106109c8576109c8613db8565b905060200201356040517f552ac3130000000000000000000000000000000000000000000000000000000081526004016100bb929190918252602082015260400190565b50600101610713565b50610a236060830183613de5565b90505f03610ab257610aad89895f818110610a4057610a40613db8565b9050602002016020810190610a559190613bb6565b868b8b8a818110610a6857610a68613db8565b9050602002016020810190610a7d9190613bb6565b84610a8b6020880188613bb6565b610a9b6040890160208a01613e46565b610aa860408a018a613de5565b611794565b610fe1565b60408051808201909152600d81527f455354494d4154455f53574150000000000000000000000000000000000000006020909101527f9fbe8fb0ae8f68c4e90aaac8dee895c5ba9596470c68a601101c9d65a103a943610b156060840184613de5565b610b1e91613bd8565b03610b58576040517f5fea03c9000000000000000000000000000000000000000000000000000000008152600481018290526024016100bb565b610aad5f8a8a8a8a8a87610b6f60608b018b613de5565b604051602001610456989796959493929190613e5f565b610b8f85613d6f565b9050855b8015610ea6575f888860018403818110610baf57610baf613db8565b610bc59260206060909202019081019150613bb6565b73ffffffffffffffffffffffffffffffffffffffff168b8b60018503818110610bf057610bf0613db8565b9050602002016020810190610c059190613bb6565b73ffffffffffffffffffffffffffffffffffffffff16148015610ca55750888860018403818110610c3857610c38613db8565b9050606002016020016020810190610c509190613bb6565b73ffffffffffffffffffffffffffffffffffffffff168b8b84818110610c7857610c78613db8565b9050602002016020810190610c8d9190613bb6565b73ffffffffffffffffffffffffffffffffffffffff16145b15610cb257506001610dc5565b888860018403818110610cc757610cc7613db8565b9050606002016020016020810190610cdf9190613bb6565b73ffffffffffffffffffffffffffffffffffffffff168b8b60018503818110610d0a57610d0a613db8565b9050602002016020810190610d1f9190613bb6565b73ffffffffffffffffffffffffffffffffffffffff16148015610dbd5750888860018403818110610d5257610d52613db8565b610d689260206060909202019081019150613bb6565b73ffffffffffffffffffffffffffffffffffffffff168b8b84818110610d9057610d90613db8565b9050602002016020810190610da59190613bb6565b73ffffffffffffffffffffffffffffffffffffffff16145b1561093f57505f5b610df18989610dd5600186613da5565b818110610de457610de4613db8565b9050606002018285611cdb565b92508585610e00600185613da5565b818110610e0f57610e0f613db8565b90506020020135831115610e7e57828686610e2b600186613da5565b818110610e3a57610e3a613db8565b905060200201356040517f97121c210000000000000000000000000000000000000000000000000000000081526004016100bb929190918252602082015260400190565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01610b93565b50610eb46060830183613de5565b90505f03610f2457610aad89895f818110610ed157610ed1613db8565b9050602002016020810190610ee69190613bb6565b828b8b8a818110610ef957610ef9613db8565b9050602002016020810190610f0e9190613bb6565b610f1789613d6f565b610a8b6020880188613bb6565b60408051808201909152600d81527f455354494d4154455f53574150000000000000000000000000000000000000006020909101527f9fbe8fb0ae8f68c4e90aaac8dee895c5ba9596470c68a601101c9d65a103a943610f876060840184613de5565b610f9091613bd8565b03610fca576040517f5fea03c9000000000000000000000000000000000000000000000000000000008152600481018290526024016100bb565b610fe15f8a8a8a8a8a87610b6f60608b018b613de5565b610fe96102b3565b98975050505050505050565b5f5f846040516020016110089190613f1d565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815291815281516020928301207fffffffffffffffff00000000000000000000000000000000000000000000000081165f908152600290935290822054909250908190036110cf576040517f722948110000000000000000000000000000000000000000000000000000000081527fffffffffffffffff000000000000000000000000000000000000000000000000831660048201526024016100bb565b608881901c670fffffffffffffff1660c482901c5f80806110f38b87898888612435565b92509250925089156112b857607e86901c601f1660098111156111325761111c60098203612844565b8a8161112a5761112a613f2b565b049950611143565b61113e81600903612844565b8a0299505b6127108a108061115a5750670fffffffffffffff8a115b15611194576040517f17d5c4d2000000000000000000000000000000000000000000000000000000008152600481018b90526024016100bb565b600283048a11156111db576040517ffecbb38c000000000000000000000000000000000000000000000000000000008152600481018b9052602481018490526044016100bb565b620f4240611fff88168b0204808b03848101908402816111fd576111fd613f2b565b0499506064600d89901c607f168202048b038701965089861015611257576040517f473f499f000000000000000000000000000000000000000000000000000000008152600481018b9052602481018790526044016100bb565b94899003946c7e37be2022c0914b2680000000878602048610156112b1576040517fb499466c00000000000000000000000000000000000000000000000000000000815260048101889052602481018790526044016100bb565b505061147d565b608386901c601f1660098111156112eb576112d560098203612844565b8a816112e3576112e3613f2b565b0499506112fc565b6112f781600903612844565b8a0299505b6127108a10806113135750670fffffffffffffff8a115b1561134d576040517f17d5c4d2000000000000000000000000000000000000000000000000000000008152600481018b90526024016100bb565b600282048a1115611394576040517ffecbb38c000000000000000000000000000000000000000000000000000000008152600481018b9052602481018390526044016100bb565b620f4240611fff88168b0204808b03838101908502816113b6576113b6613f2b565b0499506064600d89901c607f168202048b038601955089871015611410576040517f473f499f000000000000000000000000000000000000000000000000000000008152600481018b9052602481018890526044016100bb565b898703965061271085026b033b2e3c9fd0803ce800000087028161143657611436613f2b565b0487101561147a576040517fb499466c00000000000000000000000000000000000000000000000000000000815260048101889052602481018790526044016100bb565b50505b601486901c60031680156114ee575f8b6114b8578984036b033b2e3c9fd0803ce80000008c850102816114b2576114b2613f2b565b046114db565b8a84016b033b2e3c9fd0803ce80000008b850302816114d9576114d9613f2b565b045b90506114ea888a848489612927565b9150505b600181118061150457506001601688901c166001145b15611562577fffffffffffffffff00000000000000000000000000000000000000000000000088165f90815260036020526040902080547ffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0000000016421790555b50670fffffffffffffff8511806115805750670fffffffffffffff84115b156115e9576040517f9e0db8780000000000000000000000000000000000000000000000000000000081527fffffffffffffffff0000000000000000000000000000000000000000000000008816600482015260248101869052604481018590526064016100bb565b7fffffffffffffffff00000000000000000000000000000000000000000000000087165f908152600260205260409020805470ffffffffffffffffffffffffffffffffff16608887901b1760c486901b1790819055955089156116f0576040805168010000000000000000607d8b901b60418d901b60c08c901c1717178152602081018890527ffbce846c23a724e6e61161894819ec46c90a8d3dd96e90e7342c6ef49ffb539c910160405180910390a1608386901c601f1660098111156116cf576116be6116b9600983613da5565b612844565b6116c8908a613f58565b98506116ea565b6116dd6116b9826009613da5565b6116e7908a613f6f565b98505b50611786565b60408051607d8a901b60418c901b60c08b901c17178152602081018890527ffbce846c23a724e6e61161894819ec46c90a8d3dd96e90e7342c6ef49ffb539c910160405180910390a1607e86901c601f166009811115611769576117586116b9600983613da5565b611762908a613f58565b9850611784565b6117776116b9826009613da5565b611781908a613f6f565b98505b505b505050505050509392505050565b73ffffffffffffffffffffffffffffffffffffffff84166117b3573393505b7fffffffffffffffffffffffff111111111111111111111111111111111111111273ffffffffffffffffffffffffffffffffffffffff8716016117ff576117fa8486612b3f565b61180a565b61180a868587612b88565b7fffffffffffffffffffffffff111111111111111111111111111111111111111273ffffffffffffffffffffffffffffffffffffffff89160161198b57828015611852575034155b15611928576040517fb6a545480000000000000000000000000000000000000000000000000000000081524790339063b6a545489061189b908c908c9088908890600401613fa7565b5f604051808303815f87803b1580156118b2575f5ffd5b505af11580156118c4573d5f5f3e3d5ffd5b505050508781476118d59190613da5565b1015611922576118e58147613da5565b6040517fbdd289ca0000000000000000000000000000000000000000000000000000000081526004810191909152602481018990526044016100bb565b50611c4a565b8634101561196b576040517fbdd289ca000000000000000000000000000000000000000000000000000000008152346004820152602481018890526044016100bb565b8634111561198657611986336119818934613da5565b612b3f565b611c4a565b34156119c3576040517f1841b4e100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8215611c3e576040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201525f9073ffffffffffffffffffffffffffffffffffffffff8a16906370a0823190602401602060405180830381865afa158015611a33573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611a579190613b9f565b6040517fb6a54548000000000000000000000000000000000000000000000000000000008152909150339063b6a5454890611a9c908c908c9088908890600401613fa7565b5f604051808303815f87803b158015611ab3575f5ffd5b505af1158015611ac5573d5f5f3e3d5ffd5b50506040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201528a925083915073ffffffffffffffffffffffffffffffffffffffff8c16906370a0823190602401602060405180830381865afa158015611b35573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611b599190613b9f565b611b639190613da5565b1015611922576040517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152819073ffffffffffffffffffffffffffffffffffffffff8b16906370a0823190602401602060405180830381865afa158015611bd3573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611bf79190613b9f565b611c019190613da5565b6040517faf35ea9e0000000000000000000000000000000000000000000000000000000081526004810191909152602481018990526044016100bb565b611c4a8833308a612c2e565b5050505050505050565b5f611c7d7f7e8134afb5ed35d36cb65e24b9a4712a52bb77d952806c1acf50970d2107797f5490565b905073ffffffffffffffffffffffffffffffffffffffff8116611ccc576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611cd68183610271565b505050565b5f5f84604051602001611cee9190613f1d565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815291815281516020928301207fffffffffffffffff00000000000000000000000000000000000000000000000081165f90815260029093529082205490925090819003611db5576040517f722948110000000000000000000000000000000000000000000000000000000081527fffffffffffffffff000000000000000000000000000000000000000000000000831660048201526024016100bb565b608881901c670fffffffffffffff1660c482901c5f8080611dd98b87898888612435565b9250925092508915611fb557608386901c601f166009811115611e1857611e0260098203612844565b8a81611e1057611e10613f2b565b049950611e29565b611e2481600903612844565b8a0299505b6127108a1080611e405750670fffffffffffffff8a115b15611e7a576040517f17d5c4d2000000000000000000000000000000000000000000000000000000008152600481018b90526024016100bb565b600282048a1115611ec1576040517ffecbb38c000000000000000000000000000000000000000000000000000000008152600481018b9052602481018390526044016100bb565b898203838b0281611ed457611ed4613f2b565b0498505f89611fff8916620f424090810390820281611ef557611ef5613f2b565b0403998a019990506064600d89901c607f168202048a03870196508a861015611f54576040517f473f499f000000000000000000000000000000000000000000000000000000008152600481018c9052602481018790526044016100bb565b948a9003946c7e37be2022c0914b268000000087860204861015611fae576040517fb499466c00000000000000000000000000000000000000000000000000000000815260048101889052602481018790526044016100bb565b5050612191565b607e86901c601f166009811115611fe857611fd260098203612844565b8a81611fe057611fe0613f2b565b049950611ff9565b611ff481600903612844565b8a0299505b6127108a10806120105750670fffffffffffffff8a115b1561204a576040517f17d5c4d2000000000000000000000000000000000000000000000000000000008152600481018b90526024016100bb565b600283048a1115612091576040517ffecbb38c000000000000000000000000000000000000000000000000000000008152600481018b9052602481018490526044016100bb565b898303828b02816120a4576120a4613f2b565b0498505f89611fff8916620f4240908103908202816120c5576120c5613f2b565b0403998a019990506064600d89901c607f168202048a03860195508a871015612124576040517f473f499f000000000000000000000000000000000000000000000000000000008152600481018c9052602481018890526044016100bb565b8a8703965061271085026b033b2e3c9fd0803ce800000087028161214a5761214a613f2b565b0487101561218e576040517fb499466c00000000000000000000000000000000000000000000000000000000815260048101889052602481018790526044016100bb565b50505b601486901c6003168015612202575f8b6121cc578a84036b033b2e3c9fd0803ce80000008b850102816121c6576121c6613f2b565b046121ef565b8984016b033b2e3c9fd0803ce80000008c850302816121ed576121ed613f2b565b045b90506121fe888a848489612927565b9150505b600181118061221857506001601688901c166001145b15612276577fffffffffffffffff00000000000000000000000000000000000000000000000088165f90815260036020526040902080547ffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0000000016421790555b50670fffffffffffffff8511806122945750670fffffffffffffff84115b156122fd576040517f9e0db8780000000000000000000000000000000000000000000000000000000081527fffffffffffffffff0000000000000000000000000000000000000000000000008816600482015260248101869052604481018590526064016100bb565b7fffffffffffffffff00000000000000000000000000000000000000000000000087165f908152600260205260409020805470ffffffffffffffffffffffffffffffffff16608887901b1760c486901b1790819055955089156123cd576040805168010000000000000000607d8c901b60418c901b60c08c901c1717178152602081018890527ffbce846c23a724e6e61161894819ec46c90a8d3dd96e90e7342c6ef49ffb539c910160405180910390a1607e86901c601f1660098111156116cf576116be6116b9600983613da5565b60408051607d8b901b60418b901b60c08b901c17178152602081018890527ffbce846c23a724e6e61161894819ec46c90a8d3dd96e90e7342c6ef49ffb539c910160405180910390a1608386901c601f166009811115611769576117586116b9600983613da5565b5f5f5f6001601688901c165f03612570576207ffff603f88901c169250825f036124745763ffffffff601f88901c1660ff601789901c161b925061257e565b61249e7f0000000000000000000000004ec7b668baf70d4a4b0fc7941a7708a07b6d45be84612cf1565b73ffffffffffffffffffffffffffffffffffffffff166302c59a686124c660208b018b613bb6565b6124d660408c0160208d01613bb6565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b16815273ffffffffffffffffffffffffffffffffffffffff9283166004820152911660248201526044016020604051808303815f875af1158015612545573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906125699190613b9f565b925061257e565b61257b88888861315a565b92505b613fff605388901c811690606189901c16600160528a901c811690036125af576125a982828a61344e565b90925090505b5f5f83612710036127108802816125c8576125c8613f2b565b04915050612710828103870204600360148c901c165f6001821115612751578160020361266557507fffffffffffffffff0000000000000000000000000000000000000000000000008b165f90815260036020526040902054602181901c62ffffff166401ffffffff821642038181101561265a5781818c8803028161265057612650613f2b565b048b019a5061265e565b859a505b50506126df565b816003036126df57507fffffffffffffffff0000000000000000000000000000000000000000000000008b165f90815260036020526040902054602181901c62ffffff166401ffffffff82164203818110156126d8578181868d0302816126ce576126ce613f2b565b048b039a506126dc565b849a505b50505b801561275157620fffff604182901c1660ff603983901c161b91508189111561270a5781985061272b565b620fffff605d82901c1660ff605583901c161b91508189101561272b578198505b85612710036127108a028161274257612742613f2b565b0493506127108581038a020492505b6f4b3b4ca85a86c47a098a22400000000084101561277b5761277483850261354d565b91506127a7565b61279a670de0b6b3a76400008404670de0b6b3a764000086040261354d565b670de0b6b3a76400000291505b6b033b2e3c9fd0803ce80000008210156127d1576127c782858d8d6135ff565b909850965061282e565b61282982760a70c3c40a64e6c51999090b65f67d9240000000000000816127fa576127fa613f2b565b0484760a70c3c40a64e6c51999090b65f67d92400000000000008161282157612821613f2b565b048c8e6135ff565b985096505b50969c959098019a505050930195509350505050565b5f8160030361285657506103e8919050565b816009036128695750633b9aca00919050565b816001036128795750600a919050565b815f0361288857506001919050565b8160020361289857506064919050565b816004036128a95750612710919050565b816005036128bb5750620186a0919050565b816006036128cd5750620f4240919050565b816007036128df575062989680919050565b816008036128f257506305f5e100919050565b6040517f42cfde25000000000000000000000000000000000000000000000000000000008152600481018390526024016100bb565b5f613fff605387901c811690606188901c166001605289901c811690036129595761295382828961344e565b90925090505b816127100361271085028161297057612970613f2b565b0491506127108181038502049050607f607089901c81169060778a901c166001606f8b901c811690036129ae576129a882828b613678565b90925090505b6064918203868503028290048601919081038387030204850381871115612a465787600214612a41575050507fffffffffffffffff00000000000000000000000000000000000000000000000086165f90815260026020819052604090912080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcfffff16622000001790559150612b369050565b612b2e565b80871015612abe5787600314612a41575050507fffffffffffffffff00000000000000000000000000000000000000000000000086165f90815260026020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcfffff16623000001790555060039050612b36565b87600114612b2e575050507fffffffffffffffff00000000000000000000000000000000000000000000000086165f90815260026020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcfffff16621000001790555060019050612b36565b879450505050505b95945050505050565b5f5f5f5f5f8587614e20f1905080611cd6576040517fdee51a8a0000000000000000000000000000000000000000000000000000000081526201155a60048201526024016100bb565b5f6040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8416600482015282602482015260205f6044835f895af13d15601f3d1160015f511416171691505080612c28576040517fdee51a8a0000000000000000000000000000000000000000000000000000000081526201155a60048201526024016100bb565b50505050565b5f6040517f23b872dd00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8516600482015273ffffffffffffffffffffffffffffffffffffffff8416602482015282604482015260205f6064835f8a5af13d15601f3d1160015f511416171691505080612cea576040517fdee51a8a0000000000000000000000000000000000000000000000000000000081526201155960048201526024016100bb565b5050505050565b5f6060825f03612d04575f915050613154565b607f8311612dcd576040517fd60000000000000000000000000000000000000000000000000000000000000060208201527f940000000000000000000000000000000000000000000000000000000000000060218201527fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606086901b16602282015260f884901b7fff000000000000000000000000000000000000000000000000000000000000001660368201526037015b6040516020818303038152906040529050613149565b60ff8311612eaa576040517fd70000000000000000000000000000000000000000000000000000000000000060208201527f940000000000000000000000000000000000000000000000000000000000000060218201527fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606086901b1660228201527f8100000000000000000000000000000000000000000000000000000000000000603682015260f884901b7fff00000000000000000000000000000000000000000000000000000000000000166037820152603801612db7565b61ffff8311612f88576040517fd80000000000000000000000000000000000000000000000000000000000000060208201527f940000000000000000000000000000000000000000000000000000000000000060218201527fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606086901b1660228201527f820000000000000000000000000000000000000000000000000000000000000060368201527fffff00000000000000000000000000000000000000000000000000000000000060f085901b166037820152603901612db7565b62ffffff8311613067576040517fd90000000000000000000000000000000000000000000000000000000000000060208201527f940000000000000000000000000000000000000000000000000000000000000060218201527fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606086901b1660228201527f830000000000000000000000000000000000000000000000000000000000000060368201527fffffff000000000000000000000000000000000000000000000000000000000060e885901b166037820152603a01612db7565b6040517fda0000000000000000000000000000000000000000000000000000000000000060208201527f940000000000000000000000000000000000000000000000000000000000000060218201527fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606086901b1660228201527f840000000000000000000000000000000000000000000000000000000000000060368201527fffffffff0000000000000000000000000000000000000000000000000000000060e085901b166037820152603b0160405160208183030381529060405290505b805160209091012090505b92915050565b7fffffffffffffffff00000000000000000000000000000000000000000000000081165f90815260036020526040812054601f84901c63ffffffff16601785901c60ff161b906401ffffffff609982901c81169082168181116131bd57816131bf565b805b90506131f47f0000000000000000000000004ec7b668baf70d4a4b0fc7941a7708a07b6d45be6207ffff603f8a901c16612cf1565b73ffffffffffffffffffffffffffffffffffffffff166302c59a6861321c60208b018b613bb6565b61322c60408c0160208d01613bb6565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b16815273ffffffffffffffffffffffffffffffffffffffff9283166004820152911660248201526044016020604051808303815f875af115801561329b573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906132bf9190613b9f565b94505f620f4240620fffff608586901c1602824203620fffff607187901c16870202816132ee576132ee613f2b565b0490508486111561339857938401938486111561330d57849550613442565b7fffffffffffffffff00000000000000000000000000000000000000000000000087165f90815260036020908152604080832080547ffffffffffffffffffc000000000000000001ffffffffffffffffffffffffffff1690556002909152902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbfffff169055613442565b8085116133a5575f6133a9565b8085035b9450848610156133bb57849550613442565b7fffffffffffffffff00000000000000000000000000000000000000000000000087165f90815260036020908152604080832080547ffffffffffffffffffc000000000000000001ffffffffffffffffffffffffffff1690556002909152902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbfffff1690555b50505050509392505050565b7fffffffffffffffff00000000000000000000000000000000000000000000000081165f908152600460205260408120548190601c81901c620fffff16603082901c6401ffffffff168342828401101561351357505050507fffffffffffffffff00000000000000000000000000000000000000000000000083165f9081526004602090815260408083208390556002909152902080547ffffffffffffffffffffffffffffffffffffffffffffbffffffffffffffffffff1690555083905082613545565b504281900361352889613fff86168386613760565b61353c89613fff600e88901c168487613760565b95509550505050505b935093915050565b60b5817101000000000000000000000000000000000081106135745760409190911b9060801c5b690100000000000000000081106135905760209190911b9060401c5b6501000000000081106135a85760109190911b9060201c5b630100000081106135be5760089190911b9060101c5b62010000010260121c80820401600190811c80830401811c80830401811c80830401811c80830401811c80830401811c80830401901c908190048111900390565b5f8085850381600282028689026b033b2e3c9fd0803ce80000008702018161362957613629613f2b565b049050613658818202836b033b2e3c9fd0803ce8000000888a02028161365157613651613f2b565b040161354d565b01976b033b2e3c9fd0803ce8000000978902979097049695505050505050565b7fffffffffffffffff00000000000000000000000000000000000000000000000081165f908152600560205260408120548190600e81901c620fffff16602282901c6401ffffffff168342828401101561373d57505050507fffffffffffffffff00000000000000000000000000000000000000000000000083165f9081526005602090815260408083208390556002909152902080547fffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffff1690555083905082613545565b504281900361375189607f86168386613760565b61353c89607f600788901c1684875b5f83851115613786578183858703028161377c5761377c613f2b565b048401905061379f565b8183868603028161379957613799613f2b565b04840390505b949350505050565b73ffffffffffffffffffffffffffffffffffffffff811681146137c8575f5ffd5b50565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b5f5f60408385031215613809575f5ffd5b8235613814816137a7565b9150602083013567ffffffffffffffff81111561382f575f5ffd5b8301601f8101851361383f575f5ffd5b803567ffffffffffffffff811115613859576138596137cb565b6040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0603f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8501160116810181811067ffffffffffffffff821117156138c5576138c56137cb565b6040528181528282016020018710156138dc575f5ffd5b816020840160208301375f602083830101528093505050509250929050565b8035801515811461390a575f5ffd5b919050565b803561390a816137a7565b5f5f83601f84011261392a575f5ffd5b50813567ffffffffffffffff811115613941575f5ffd5b602083019150836020828501011115613958575f5ffd5b9250929050565b5f5f5f5f5f5f5f5f5f5f8a8c0361014081121561397a575f5ffd5b6060811215613987575f5ffd5b508a995061399760608c016138fb565b985060808b0135975060a08b013596506139b360c08c0161390f565b95506139c160e08c016138fb565b94506101008b013567ffffffffffffffff8111156139dd575f5ffd5b6139e98d828e0161391a565b9095509350506101208b013567ffffffffffffffff811115613a09575f5ffd5b613a158d828e0161391a565b915080935050809150509295989b9194979a5092959850565b5f5f83601f840112613a3e575f5ffd5b50813567ffffffffffffffff811115613a55575f5ffd5b6020830191508360208260051b8501011115613958575f5ffd5b5f60808284031215613a7f575f5ffd5b50919050565b5f5f5f5f5f5f5f5f60a0898b031215613a9c575f5ffd5b883567ffffffffffffffff811115613ab2575f5ffd5b613abe8b828c01613a2e565b909950975050602089013567ffffffffffffffff811115613add575f5ffd5b8901601f81018b13613aed575f5ffd5b803567ffffffffffffffff811115613b03575f5ffd5b8b6020606083028401011115613b17575f5ffd5b6020919091019650945060408901359350606089013567ffffffffffffffff811115613b41575f5ffd5b613b4d8b828c01613a2e565b909450925050608089013567ffffffffffffffff811115613b6c575f5ffd5b613b788b828c01613a6f565b9150509295985092959890939650565b5f60208284031215613b98575f5ffd5b5035919050565b5f60208284031215613baf575f5ffd5b5051919050565b5f60208284031215613bc6575f5ffd5b8135613bd1816137a7565b9392505050565b80356020831015613154577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff602084900360031b1b1692915050565b8035613c1f816137a7565b73ffffffffffffffffffffffffffffffffffffffff1682526020810135613c45816137a7565b73ffffffffffffffffffffffffffffffffffffffff166020830152604090810135910152565b81835281816020850137505f602082840101525f60207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b613cbc8188613c14565b85151560608201528460808201528360a082015260e060c08201525f610fe960e083018486613c6b565b8215158152604060208201525f82518060408401528060208501606085015e5f6060828501015260607fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8301168401019150509392505050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f7f80000000000000000000000000000000000000000000000000000000000000008203613d9f57613d9f613d42565b505f0390565b8181038181111561315457613154613d42565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5f5f83357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112613e18575f5ffd5b83018035915067ffffffffffffffff821115613e32575f5ffd5b602001915036819003821315613958575f5ffd5b5f60208284031215613e56575f5ffd5b613bd1826138fb565b60a080825281018890525f8960c08301825b8b811015613eae578235613e84816137a7565b73ffffffffffffffffffffffffffffffffffffffff16825260209283019290910190600101613e71565b50838103602080860191909152898252019050885f805b8a811015613eea57613ed78484613c14565b6060938401939290920191600101613ec5565b50505060408301879052606083018690528281036080840152613f0e818587613c6b565b9b9a5050505050505050505050565b606081016131548284613c14565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b808202811582820484141761315457613154613d42565b5f82613fa2577f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b500490565b73ffffffffffffffffffffffffffffffffffffffff85168152836020820152606060408201525f613fdc606083018486613c6b565b969550505050505056fea264697066735822122048314b8dec15e796decf36eb636ff0c09330021781b58dfce741aa70a3f264ab64736f6c634300081d0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000008a5b57d047d284a2a6ec774a8bca2beab2a6c95500000000000000000000000052aa899454998be5b000ad077a46bbe360f4e4970000000000000000000000004ec7b668baf70d4a4b0fc7941a7708a07b6d45be
-----Decoded View---------------
Arg [0] : auth_ (address): 0x8a5B57d047D284A2A6ec774A8BCA2BeaB2A6C955
Arg [1] : liquidity_ (address): 0x52Aa899454998Be5b000Ad077a46Bbe360F4e497
Arg [2] : deployerContract_ (address): 0x4EC7b668BAF70d4A4b0FC7941a7708A07b6d45Be
-----Encoded View---------------
3 Constructor Arguments found :
Arg [0] : 0000000000000000000000008a5b57d047d284a2a6ec774a8bca2beab2a6c955
Arg [1] : 00000000000000000000000052aa899454998be5b000ad077a46bbe360f4e497
Arg [2] : 0000000000000000000000004ec7b668baf70d4a4b0fc7941a7708a07b6d45be
Loading...
Loading
Loading...
Loading
Net Worth in USD
$10,159.01
Net Worth in ETH
5.141952
Token Allocations
USDT
50.98%
USDC
33.14%
USDE
15.84%
Others
0.04%
Multichain Portfolio | 34 Chains
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.