Description:
Multi-signature wallet contract requiring multiple confirmations for transaction execution.
Blockchain: Ethereum
Source Code: View Code On The Blockchain
Solidity Source Code:
{{
"language": "Solidity",
"sources": {
"src/Assistant.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Interfaces
import {IQuoter} from "./interfaces/IQuoter.sol";
import {IVault} from "core/interfaces/IVault.sol";
import {IOracle} from "core/interfaces/IOracle.sol";
import {IUniswapV3Pool} from "v3-core/interfaces/IUniswapV3Pool.sol";
// Contracts and libraries
import {SirStructs} from "core/libraries/SirStructs.sol";
import {SystemConstants} from "core/libraries/SystemConstants.sol";
import {FullMath} from "core/libraries/FullMath.sol";
import {IWETH9, IERC20} from "core/interfaces/IWETH9.sol";
import {UniswapPoolAddress} from "core/libraries/UniswapPoolAddress.sol";
import {AddressClone} from "core/libraries/AddressClone.sol";
import {TickMath} from "v3-core/libraries/TickMath.sol";
import "forge-std/console.sol";
/**
* @notice Helper functions for SIR protocol
*/
contract Assistant {
IVault public immutable VAULT;
IOracle private immutable SIR_ORACLE;
address private immutable UNISWAPV3_FACTORY;
IQuoter private immutable UNISWAPV3_QUOTER;
error VaultDoesNotExist();
error AmountTooLow();
error TooMuchCollateral();
error TEAMaxSupplyExceeded();
enum VaultStatus {
InvalidVault,
NoUniswapPool,
VaultCanBeCreated,
VaultAlreadyExists
}
constructor(address vault, address oracle, address uniswapV3Factory) {
VAULT = IVault(vault);
SIR_ORACLE = IOracle(oracle);
UNISWAPV3_FACTORY = uniswapV3Factory;
if (block.chainid == 1) UNISWAPV3_QUOTER = IQuoter(0x5e55C9e631FAE526cd4B0526C4818D6e0a9eF0e3);
else if (block.chainid == 11155111) UNISWAPV3_QUOTER = IQuoter(0xe3c07ebF66b9D070b589bCCa30903891F71A92Be);
else revert("Network not supported");
}
/**
* @notice It returns the reserves of the vaults specified in vaultIds
*/
function getReserves(uint48[] calldata vaultIds) external view returns (SirStructs.Reserves[] memory reserves) {
reserves = new SirStructs.Reserves[](vaultIds.length);
SirStructs.VaultParameters memory vaultParams;
for (uint256 i = 0; i < vaultIds.length; i++) {
vaultParams = VAULT.paramsById(vaultIds[i]);
reserves[i] = VAULT.getReserves(vaultParams);
}
}
/**
* @notice It returns the balances of the user in vaults [offset + 1, offset + numVaults].
* @param user The address of the user.
* @param offset The offset of the vaults.
* @param numVaults The number of vaults.
*/
function getUserBalances(
address user,
uint offset,
uint numVaults
)
external
view
returns (uint256[] memory apeBalances, uint256[] memory teaBalances, uint80[] memory unclaimedSirRewards)
{
IERC20 ape;
apeBalances = new uint256[](numVaults);
teaBalances = new uint256[](numVaults);
unclaimedSirRewards = new uint80[](numVaults);
for (uint256 vaultId = offset + 1; vaultId <= offset + numVaults; vaultId++) {
ape = IERC20(AddressClone.getAddress(address(VAULT), vaultId));
apeBalances[vaultId - offset - 1] = ape.balanceOf(user);
teaBalances[vaultId - offset - 1] = VAULT.balanceOf(user, vaultId);
unclaimedSirRewards[vaultId - offset - 1] = VAULT.unclaimedRewards(vaultId, user);
}
}
/**
* @notice It returns the ideal price of TEA.
* To get the price as [units of Collateral][per unit of TEA], divide num by den.
*/
function priceOfTEA(
SirStructs.VaultParameters calldata vaultParams
) external view returns (uint256 num, uint256 den) {
// Get current reserves
SirStructs.Reserves memory reserves = VAULT.getReserves(vaultParams);
num = reserves.reserveLPers;
// Get supply of TEA
SirStructs.VaultState memory vaultState = VAULT.vaultStates(vaultParams);
den = VAULT.totalSupply(vaultState.vaultId);
}
/**
* @notice It returns the price of the APE token.
* To get the price as [units of Collateral][per unit of APE], divide num by den.
*/
function priceOfAPE(
SirStructs.VaultParameters calldata vaultParams
) external view returns (uint256 num, uint256 den) {
// Get current reserves
SirStructs.Reserves memory reserves = VAULT.getReserves(vaultParams);
// Get system parameters
SirStructs.SystemParameters memory systemParams = VAULT.systemParams();
// Substract fees
num = _feeAPE(reserves.reserveApes, systemParams.baseFee.fee, vaultParams.leverageTier);
// Get supply of APE
SirStructs.VaultState memory vaultState = VAULT.vaultStates(vaultParams);
den = IERC20(getAddressAPE(vaultState.vaultId)).totalSupply();
}
/**
* @notice It returns the status of the vault.
* 0: InvalidVault - returned when the ERC20 tokens are not valid.
* 1: NoUniswapPool - returned when no Uniswap pool of the two tokens does not exist.
* 2: VaultCanBeCreated - vault does not exist and it can be created.
* 3: VaultAlreadyExists - vault already exists.
*/
function getVaultStatus(SirStructs.VaultParameters calldata vaultParams) external view returns (VaultStatus) {
// Check if the token addresses are a smart contract
if (vaultParams.collateralToken.code.length == 0) return VaultStatus.InvalidVault;
if (vaultParams.debtToken.code.length == 0) return VaultStatus.InvalidVault;
// Check if the token returns total supply
(bool success, ) = vaultParams.collateralToken.staticcall(abi.encodeWithSelector(IERC20.totalSupply.selector));
if (!success) return VaultStatus.InvalidVault;
(success, ) = vaultParams.debtToken.staticcall(abi.encodeWithSelector(IERC20.totalSupply.selector));
if (!success) return VaultStatus.InvalidVault;
// Check if the leverage tier is valid
if (
vaultParams.leverageTier < SystemConstants.MIN_LEVERAGE_TIER ||
vaultParams.leverageTier > SystemConstants.MAX_LEVERAGE_TIER
) return VaultStatus.InvalidVault;
// Check if a Uniswap pool exists
if (
!_checkFeeTierExists(vaultParams, 100) &&
!_checkFeeTierExists(vaultParams, 500) &&
!_checkFeeTierExists(vaultParams, 3000) &&
!_checkFeeTierExists(vaultParams, 10000)
) return VaultStatus.NoUniswapPool;
// Check if vault already exists
SirStructs.VaultState memory vaultState = VAULT.vaultStates(vaultParams);
if (vaultState.vaultId == 0) return VaultStatus.VaultCanBeCreated;
return VaultStatus.VaultAlreadyExists;
}
function getAddressAPE(uint48 vaultId) public view returns (address) {
return AddressClone.getAddress(address(VAULT), vaultId);
}
/*////////////////////////////////////////////////////////////////
QUOTE FUNCTIONS
////////////////////////////////////////////////////////////////*/
/**
* @notice It returns the amount of TEA/APE tokens that would be obtained by depositing collateral token.
* @dev If quoteMint reverts, mint will revert as well; vice versa is not necessarily true.
* @return amountTokens that would be obtained by depositing amountCollateral.
*/
function quoteMint(
bool isAPE,
SirStructs.VaultParameters calldata vaultParams,
uint144 amountCollateral
) public view returns (uint256 amountTokens) {
// Get vault state
SirStructs.VaultState memory vaultState = VAULT.vaultStates(vaultParams);
if (vaultState.vaultId == 0) revert VaultDoesNotExist();
if (amountCollateral == 0) revert AmountTooLow();
// Get current reserves
SirStructs.Reserves memory reserves = VAULT.getReserves(vaultParams);
SirStructs.SystemParameters memory systemParams = VAULT.systemParams();
if (isAPE) {
// Compute how much collateral actually gets deposited
uint256 collateralIn = _feeAPE(amountCollateral, systemParams.baseFee.fee, vaultParams.leverageTier);
// Get supply of APE
address ape = getAddressAPE(vaultState.vaultId);
uint256 supplyAPE = IERC20(ape).totalSupply();
// Calculate tokens
amountTokens = supplyAPE == 0
? collateralIn + reserves.reserveApes
: FullMath.mulDiv(supplyAPE, collateralIn, reserves.reserveApes);
} else {
// Get collateralIn
uint256 collateralIn = _feeMintTEA(amountCollateral, systemParams.lpFee.fee);
// Get supply of TEA
uint256 supplyTEA = VAULT.totalSupply(vaultState.vaultId);
// Calculate tokens
amountTokens = supplyTEA == 0
? _amountFirstMint(vaultParams.collateralToken, amountCollateral + reserves.reserveLPers)
: FullMath.mulDiv(supplyTEA, amountCollateral, reserves.reserveLPers);
// Check that total supply does not overflow
if (amountTokens > SystemConstants.TEA_MAX_SUPPLY - supplyTEA) revert TEAMaxSupplyExceeded();
// Minter's share of TEA
amountTokens = FullMath.mulDiv(
amountTokens,
collateralIn,
supplyTEA == 0
? amountCollateral + reserves.reserveLPers // In the first mint, reserveLPers contains orphaned fees from apes
: amountCollateral
);
}
if (amountTokens == 0) revert AmountTooLow();
}
/**
* @notice It returns the amount of TEA/APE tokens that would be obtained by depositing debt token
* @dev If quoteMint reverts, mint will revert as well; vice versa is not necessarily true.
* @return amountTokens that would be obtained.
*/
function quoteMintWithDebtToken(
bool isAPE,
SirStructs.VaultParameters calldata vaultParams,
uint256 amountDebtToken
) external view returns (uint256 amountTokens, uint256 amountCollateral, uint256 amountCollateralIdeal) {
if (amountDebtToken == 0) revert AmountTooLow();
// Get fee tier
uint24 feeTier = SIR_ORACLE.uniswapFeeTierOf(vaultParams.debtToken, vaultParams.collateralToken);
// Quote Uniswap v3
(amountCollateral, , , ) = UNISWAPV3_QUOTER.quoteExactInputSingle(
IQuoter.QuoteExactInputSingleParams({
tokenIn: vaultParams.debtToken,
tokenOut: vaultParams.collateralToken,
amountIn: amountDebtToken,
fee: feeTier,
sqrtPriceLimitX96: 0
})
);
// Check that amountCollateral does not overflow
if (amountCollateral > type(uint144).max) revert TooMuchCollateral();
// Calculate ideal collateral amount using instant pool price (no slippage)
// Get Uniswap pool
address uniswapPool = SIR_ORACLE.uniswapFeeTierAddressOf(vaultParams.debtToken, vaultParams.collateralToken);
// Get current price
(uint160 sqrtPriceX96, , , , , , ) = IUniswapV3Pool(uniswapPool).slot0();
// Calculate price fraction with better precision if it doesn't overflow when multiplied by itself
bool inverse = vaultParams.collateralToken == IUniswapV3Pool(uniswapPool).token1();
if (sqrtPriceX96 <= type(uint128).max) {
uint256 priceX192 = uint256(sqrtPriceX96) * sqrtPriceX96;
amountCollateralIdeal = inverse
? FullMath.mulDiv(priceX192, amountDebtToken, 1 << 192)
: FullMath.mulDiv(1 << 192, amountDebtToken, priceX192);
} else {
uint256 priceX128 = FullMath.mulDiv(sqrtPriceX96, sqrtPriceX96, 1 << 64);
amountCollateralIdeal = inverse
? FullMath.mulDiv(priceX128, amountDebtToken, 1 << 128)
: FullMath.mulDiv(1 << 128, amountDebtToken, priceX128);
}
// Given that we know how much collateral we will get from Uniswap, we can now use the quoteMint function
amountTokens = quoteMint(isAPE, vaultParams, uint144(amountCollateral));
}
function quoteCollateralToDebtToken(
address debtToken,
address collateralToken,
uint256 amountCollateral
) external view returns (uint256 amountDebtToken) {
// Get Uniswap pool
address uniswapPool = SIR_ORACLE.uniswapFeeTierAddressOf(debtToken, collateralToken);
// Get current price
(uint160 sqrtPriceX96, , , , , , ) = IUniswapV3Pool(uniswapPool).slot0();
// Calculate price fraction with better precision if it doesn't overflow when multiplied by itself
bool inverse = collateralToken == IUniswapV3Pool(uniswapPool).token1();
if (sqrtPriceX96 <= type(uint128).max) {
uint256 priceX192 = uint256(sqrtPriceX96) * sqrtPriceX96;
return
!inverse
? FullMath.mulDiv(priceX192, amountCollateral, 1 << 192)
: FullMath.mulDiv(1 << 192, amountCollateral, priceX192);
} else {
uint256 priceX128 = FullMath.mulDiv(sqrtPriceX96, sqrtPriceX96, 1 << 64);
return
!inverse
? FullMath.mulDiv(priceX128, amountCollateral, 1 << 128)
: FullMath.mulDiv(1 << 128, amountCollateral, priceX128);
}
}
/**
* @notice If quoteBurn reverts, burn in Vault.sol will revert as well; vice versa is not necessarily true.
* @return amountCollateral that would be obtained by burning amountTokens.
* @return amountDebtToken the equivalent amount in debt token using Oracle TWAP price.
*/
function quoteBurn(
bool isAPE,
SirStructs.VaultParameters calldata vaultParams,
uint256 amountTokens
) external view returns (uint144 amountCollateral, uint256 amountDebtToken) {
// Get vault state
SirStructs.VaultState memory vaultState = VAULT.vaultStates(vaultParams);
if (vaultState.vaultId == 0) revert VaultDoesNotExist();
if (amountTokens == 0) revert AmountTooLow();
// Get current reserves
SirStructs.Reserves memory reserves = VAULT.getReserves(vaultParams);
if (isAPE) {
// Get supply of APE
address ape = getAddressAPE(vaultState.vaultId);
uint256 supplyAPE = IERC20(ape).totalSupply();
// Get collateralOut
uint144 collateralOut = uint144(FullMath.mulDiv(reserves.reserveApes, amountTokens, supplyAPE));
// Get system parameters
SirStructs.SystemParameters memory systemParams = VAULT.systemParams();
// Get collateral withdrawn
amountCollateral = _feeAPE(collateralOut, systemParams.baseFee.fee, vaultParams.leverageTier);
} else {
// Get supply of TEA
uint256 supplyTEA = VAULT.totalSupply(vaultState.vaultId);
// Get amount of collateral that would be withdrawn
amountCollateral = uint144(FullMath.mulDiv(reserves.reserveLPers, amountTokens, supplyTEA));
}
// Convert collateral amount to debt token amount using Oracle TWAP
amountDebtToken = _convertCollateralToDebtTokenUsingTWAP(
vaultParams.collateralToken,
vaultParams.debtToken,
amountCollateral
);
}
/**
* @notice Converts collateral amount to debt token amount using the Oracle's TWAP price.
* @dev This uses the same TWAP price calculation as the Oracle contract for consistency.
* @param collateralToken The collateral token address.
* @param debtToken The debt token address.
* @param amountCollateral The amount of collateral to convert.
* @return amountDebtToken The equivalent amount in debt tokens.
*/
function _convertCollateralToDebtTokenUsingTWAP(
address collateralToken,
address debtToken,
uint256 amountCollateral
) private view returns (uint256 amountDebtToken) {
// Get the pool address from Oracle
address poolAddress = SIR_ORACLE.uniswapFeeTierAddressOf(collateralToken, debtToken);
IUniswapV3Pool pool = IUniswapV3Pool(poolAddress);
// Get TWAP observation data similar to Oracle
uint32[] memory secondsAgos = new uint32[](2);
secondsAgos[0] = 1800; // 30 minutes (TWAP_DURATION from Oracle)
secondsAgos[1] = 0;
int56[] memory tickCumulatives;
try pool.observe(secondsAgos) returns (int56[] memory tickCumulatives_, uint160[] memory) {
tickCumulatives = tickCumulatives_;
} catch {
// If 30-minute TWAP not available, try to get the oldest available observation
// This mimics Oracle's fallback behavior
(, , uint16 observationIndex, uint16 observationCardinality, , , ) = pool.slot0();
if (observationCardinality > 1) {
// Get oldest observation
uint32 oldestObservationSeconds;
int56 oldestTickCumulative;
bool initialized;
// Try to get the oldest initialized observation
uint16 oldestIndex = (observationIndex + 1) % observationCardinality;
(oldestObservationSeconds, oldestTickCumulative, , initialized) = pool.observations(oldestIndex);
if (!initialized) {
// Fallback to index 0 which is always initialized
(oldestObservationSeconds, oldestTickCumulative, , ) = pool.observations(0);
}
// Calculate time difference
uint32 timeElapsed = uint32(block.timestamp) - oldestObservationSeconds;
if (timeElapsed > 0) {
// Get current observation
secondsAgos[0] = timeElapsed;
tickCumulatives = new int56[](2);
(tickCumulatives, ) = pool.observe(secondsAgos);
} else {
// Use spot price if no TWAP available
(, int24 currentTick, , , , , ) = pool.slot0();
tickCumulatives = new int56[](2);
tickCumulatives[0] = currentTick;
tickCumulatives[1] = currentTick;
secondsAgos[0] = 1; // Avoid division by zero
}
} else {
// Use spot price if cardinality is 1
(, int24 currentTick, , , , , ) = pool.slot0();
tickCumulatives = new int56[](2);
tickCumulatives[0] = currentTick;
tickCumulatives[1] = currentTick;
secondsAgos[0] = 1; // Avoid division by zero
}
}
// Calculate average tick over the period
int24 arithmeticMeanTick = int24((tickCumulatives[1] - tickCumulatives[0]) / int56(uint56(secondsAgos[0])));
// Convert tick to price
// The price is in terms of token1/token0 in the pool
bool collateralIsToken0 = collateralToken < debtToken;
// Calculate sqrt price from tick
uint160 sqrtPriceX96 = TickMath.getSqrtRatioAtTick(arithmeticMeanTick);
// Calculate the amount of debt tokens
// Price calculation depends on token order in the pool
if (collateralIsToken0) {
// collateral is token0, debt is token1
// Price is debt/collateral (token1/token0)
// amountDebtToken = amountCollateral * price
if (sqrtPriceX96 <= type(uint128).max) {
uint256 priceX192 = uint256(sqrtPriceX96) * sqrtPriceX96;
amountDebtToken = FullMath.mulDiv(amountCollateral, priceX192, 1 << 192);
} else {
uint256 priceX128 = FullMath.mulDiv(sqrtPriceX96, sqrtPriceX96, 1 << 64);
amountDebtToken = FullMath.mulDiv(amountCollateral, priceX128, 1 << 128);
}
} else {
// collateral is token1, debt is token0
// Price is still token1/token0, but we need debt/collateral
// So we need to invert: amountDebtToken = amountCollateral / price
if (sqrtPriceX96 <= type(uint128).max) {
uint256 priceX192 = uint256(sqrtPriceX96) * sqrtPriceX96;
amountDebtToken = FullMath.mulDiv(amountCollateral, 1 << 192, priceX192);
} else {
uint256 priceX128 = FullMath.mulDiv(sqrtPriceX96, sqrtPriceX96, 1 << 64);
amountDebtToken = FullMath.mulDiv(amountCollateral, 1 << 128, priceX128);
}
}
}
/*////////////////////////////////////////////////////////////////
PRIVATE FUNCTIONS
////////////////////////////////////////////////////////////////*/
function _feeAPE(
uint144 collateralDepositedOrOut,
uint16 baseFee,
int256 leverageTier
) private pure returns (uint144 collateralInOrWithdrawn) {
unchecked {
uint256 feeNum;
uint256 feeDen;
if (leverageTier >= 0) {
feeNum = 10000; // baseFee is uint16, leverageTier is int8, so feeNum does not require more than 24 bits
feeDen = 10000 + (uint256(baseFee) << uint256(leverageTier));
} else {
uint256 temp = 10000 << uint256(-leverageTier);
feeNum = temp;
feeDen = temp + uint256(baseFee);
}
collateralInOrWithdrawn = uint144((uint256(collateralDepositedOrOut) * feeNum) / feeDen);
}
}
function _feeMintTEA(uint144 collateralDeposited, uint16 lpFee) private pure returns (uint144 collateralIn) {
unchecked {
uint256 feeNum = 10000;
uint256 feeDen = 10000 + uint256(lpFee);
collateralIn = uint144((uint256(collateralDeposited) * feeNum) / feeDen);
}
}
function _checkFeeTierExists(
SirStructs.VaultParameters calldata vaultParams,
uint24 feeTier
) private view returns (bool) {
return
UniswapPoolAddress
.computeAddress(
UNISWAPV3_FACTORY,
UniswapPoolAddress.getPoolKey(vaultParams.collateralToken, vaultParams.debtToken, feeTier)
)
.code
.length != 0;
}
function _amountFirstMint(address collateral, uint144 collateralDeposited) private view returns (uint256 amount) {
uint256 collateralTotalSupply = IERC20(collateral).totalSupply();
amount = collateralTotalSupply > SystemConstants.TEA_MAX_SUPPLY / 1e6
? FullMath.mulDiv(SystemConstants.TEA_MAX_SUPPLY, collateralDeposited, collateralTotalSupply)
: collateralDeposited * 1e6;
}
}
"
},
"src/interfaces/IQuoter.sol": {
"content": "// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >0.7.6;
pragma abicoder v2;
/// @title QuoterV2 Interface
/// @notice Supports quoting the calculated amounts from exact input or exact output swaps.
/// @notice For each pool also tells you the number of initialized ticks crossed and the sqrt price of the pool after the swap.
/// @dev These functions are not marked view because they rely on calling non-view functions and reverting
/// to compute the result. They are also not gas efficient and should not be called on-chain.
interface IQuoter {
/// @notice Returns the amount out received for a given exact input swap without executing the swap
/// @param path The path of the swap, i.e. each token pair and the pool fee
/// @param amountIn The amount of the first token to swap
/// @return amountOut The amount of the last token that would be received
/// @return sqrtPriceX96AfterList List of the sqrt price after the swap for each pool in the path
/// @return initializedTicksCrossedList List of number of initialized ticks loaded
function quoteExactInput(
bytes memory path,
uint256 amountIn
)
external
view
returns (
uint256 amountOut,
uint160[] memory sqrtPriceX96AfterList,
uint32[] memory initializedTicksCrossedList,
uint256 gasEstimate
);
struct QuoteExactInputSingleWithPoolParams {
address tokenIn;
address tokenOut;
uint256 amountIn;
address pool;
uint24 fee;
uint160 sqrtPriceLimitX96;
}
/// @notice Returns the amount out received for a given exact input but for a swap of a single pool
/// @param params The params for the quote, encoded as `quoteExactInputSingleWithPool`
/// tokenIn The token being swapped in
/// amountIn The desired input amount
/// tokenOut The token being swapped out
/// fee The fee of the pool to consider for the pair
/// pool The address of the pool to consider for the pair
/// sqrtPriceLimitX96 The price limit of the pool that cannot be exceeded by the swap
/// @return amountOut The amount of `tokenOut` that would be received
/// @return sqrtPriceX96After The sqrt price of the pool after the swap
/// @return initializedTicksCrossed The number of initialized ticks loaded
function quoteExactInputSingleWithPool(
QuoteExactInputSingleWithPoolParams memory params
)
external
view
returns (uint256 amountOut, uint160 sqrtPriceX96After, uint32 initializedTicksCrossed, uint256 gasEstimate);
struct QuoteExactInputSingleParams {
address tokenIn;
address tokenOut;
uint256 amountIn;
uint24 fee;
uint160 sqrtPriceLimitX96;
}
/// @notice Returns the amount out received for a given exact input but for a swap of a single pool
/// @param params The params for the quote, encoded as `QuoteExactInputSingleParams`
/// tokenIn The token being swapped in
/// amountIn The desired input amount
/// tokenOut The token being swapped out
/// fee The fee of the token pool to consider for the pair
/// sqrtPriceLimitX96 The price limit of the pool that cannot be exceeded by the swap
/// @return amountOut The amount of `tokenOut` that would be received
/// @return sqrtPriceX96After The sqrt price of the pool after the swap
/// @return initializedTicksCrossed The number of initialized ticks loaded
function quoteExactInputSingle(
QuoteExactInputSingleParams memory params
)
external
view
returns (uint256 amountOut, uint160 sqrtPriceX96After, uint32 initializedTicksCrossed, uint256 gasEstimate);
struct QuoteExactOutputSingleWithPoolParams {
address tokenIn;
address tokenOut;
uint256 amount;
uint24 fee;
address pool;
uint160 sqrtPriceLimitX96;
}
/// @notice Returns the amount in required to receive the given exact output amount but for a swap of a single pool
/// @param params The params for the quote, encoded as `QuoteExactOutputSingleWithPoolParams`
/// tokenIn The token being swapped in
/// tokenOut The token being swapped out
/// amount The desired output amount
/// fee The fee of the token pool to consider for the pair
/// pool The address of the pool to consider for the pair
/// sqrtPriceLimitX96 The price limit of the pool that cannot be exceeded by the swap
/// @return amountIn The amount required as the input for the swap in order to receive `amountOut`
/// @return sqrtPriceX96After The sqrt price of the pool after the swap
/// @return initializedTicksCrossed The number of initialized ticks loaded
function quoteExactOutputSingleWithPool(
QuoteExactOutputSingleWithPoolParams memory params
)
external
view
returns (uint256 amountIn, uint160 sqrtPriceX96After, uint32 initializedTicksCrossed, uint256 gasEstimate);
struct QuoteExactOutputSingleParams {
address tokenIn;
address tokenOut;
uint256 amount;
uint24 fee;
uint160 sqrtPriceLimitX96;
}
/// @notice Returns the amount in required to receive the given exact output amount but for a swap of a single pool
/// @param params The params for the quote, encoded as `QuoteExactOutputSingleParams`
/// tokenIn The token being swapped in
/// tokenOut The token being swapped out
/// amountOut The desired output amount
/// fee The fee of the token pool to consider for the pair
/// sqrtPriceLimitX96 The price limit of the pool that cannot be exceeded by the swap
/// @return amountIn The amount required as the input for the swap in order to receive `amountOut`
/// @return sqrtPriceX96After The sqrt price of the pool after the swap
/// @return initializedTicksCrossed The number of initialized ticks loaded
function quoteExactOutputSingle(
QuoteExactOutputSingleParams memory params
)
external
view
returns (uint256 amountIn, uint160 sqrtPriceX96After, uint32 initializedTicksCrossed, uint256 gasEstimate);
/// @notice Returns the amount in required for a given exact output swap without executing the swap
/// @param path The path of the swap, i.e. each token pair and the pool fee. Path must be provided in reverse order
/// @param amountOut The amount of the last token to receive
/// @return amountIn The amount of first token required to be paid
/// @return sqrtPriceX96AfterList List of the sqrt price after the swap for each pool in the path
/// @return initializedTicksCrossedList List of the initialized ticks that the swap crossed for each pool in the path
function quoteExactOutput(
bytes memory path,
uint256 amountOut
)
external
view
returns (
uint256 amountIn,
uint160[] memory sqrtPriceX96AfterList,
uint32[] memory initializedTicksCrossedList,
uint256 gasEstimate
);
}
"
},
"lib/Core/src/interfaces/IVault.sol": {
"content": "// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
import {SirStructs} from "../libraries/SirStructs.sol";
interface IVault {
error AmountTooLow();
error DeadlineExceeded();
error ExcessiveDeposit();
error InsufficientCollateralReceivedFromUniswap();
error InsufficientDeposit();
error LengthMismatch();
error LeverageTierOutOfRange();
error Locked();
error NotAWETHVault();
error NotAuthorized();
error StringsInsufficientHexLength(uint256 value, uint256 length);
error TEAMaxSupplyExceeded();
error TransferToZeroAddress();
error UnsafeRecipient();
error VaultAlreadyInitialized();
error VaultDoesNotExist();
event ApprovalForAll(address indexed account, address indexed operator, bool approved);
event ReservesChanged(uint48 indexed vaultId, bool isAPE, bool isMint, uint144 reserveLPers, uint144 reserveApes);
event TransferBatch(
address indexed operator,
address indexed from,
address indexed to,
uint256[] vaultIds,
uint256[] amounts
);
event TransferSingle(
address indexed operator,
address indexed from,
address indexed to,
uint256 id,
uint256 amount
);
event URI(string value, uint256 indexed id);
event VaultInitialized(
address indexed debtToken,
address indexed collateralToken,
int8 indexed leverageTier,
uint256 vaultId,
address ape
);
event VaultNewTax(uint48 indexed vault, uint8 tax, uint16 cumulativeTax);
function APE_IMPLEMENTATION() external view returns (address);
function ORACLE() external view returns (address);
function SIR() external view returns (address);
function SYSTEM_CONTROL() external view returns (address);
function TIMESTAMP_ISSUANCE_START() external view returns (uint40);
function balanceOf(address account, uint256 vaultId) external view returns (uint256);
function balanceOfBatch(
address[] memory owners,
uint256[] memory vaultIds
) external view returns (uint256[] memory balances_);
function burn(
bool isAPE,
SirStructs.VaultParameters memory vaultParams,
uint256 amount,
uint40 deadline
) external returns (uint144);
function claimSIR(uint256 vaultId, address lper) external returns (uint80);
function cumulativeSIRPerTEA(uint256 vaultId) external view returns (uint176 cumulativeSIRPerTEAx96);
function getReserves(
SirStructs.VaultParameters memory vaultParams
) external view returns (SirStructs.Reserves memory);
function initialize(SirStructs.VaultParameters memory vaultParams) external;
function isApprovedForAll(address, address) external view returns (bool);
function mint(
bool isAPE,
SirStructs.VaultParameters memory vaultParams,
uint256 amountToDeposit,
uint144 collateralToDepositMin,
uint40 deadline
) external payable returns (uint256 amount);
function numberOfVaults() external view returns (uint48);
function paramsById(uint48 vaultId) external view returns (SirStructs.VaultParameters memory);
function safeBatchTransferFrom(
address from,
address to,
uint256[] memory vaultIds,
uint256[] memory amounts,
bytes memory data
) external;
function safeTransferFrom(address from, address to, uint256 vaultId, uint256 amount, bytes memory data) external;
function setApprovalForAll(address operator, bool approved) external;
function supportsInterface(bytes4 interfaceId) external pure returns (bool);
function systemParams() external view returns (SirStructs.SystemParameters memory systemParams_);
function totalReserves(address collateral) external view returns (uint256);
function totalSupply(uint256 vaultId) external view returns (uint256);
function unclaimedRewards(uint256 vaultId, address lper) external view returns (uint80);
function uniswapV3SwapCallback(int256 amount0Delta, int256 amount1Delta, bytes memory data) external;
function updateSystemState(uint16 baseFee, uint16 lpFee, bool mintingStopped) external;
function updateVaults(
uint48[] memory oldVaults,
uint48[] memory newVaults,
uint8[] memory newTaxes,
uint16 cumulativeTax
) external;
function uri(uint256 vaultId) external view returns (string memory);
function vaultStates(
SirStructs.VaultParameters memory vaultParams
) external view returns (SirStructs.VaultState memory);
function vaultTax(uint48 vaultId) external view returns (uint8);
function withdrawFees(address token) external returns (uint256 totalFeesToStakers);
function withdrawToSaveSystem(address[] memory tokens, address to) external returns (uint256[] memory amounts);
}
"
},
"lib/Core/src/interfaces/IOracle.sol": {
"content": "// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
import {SirStructs} from "../libraries/SirStructs.sol";
interface IOracle {
error NoUniswapPool();
error OracleNotInitialized();
error UniswapFeeTierIndexOutOfBounds();
event OracleFeeTierChanged(uint24 feeTierPrevious, uint24 feeTierSelected);
event OracleInitialized(
address indexed token0,
address indexed token1,
uint24 feeTierSelected,
uint136 avLiquidity,
uint40 period
);
event PriceUpdated(address indexed token0, address indexed token1, bool priceTruncated, int64 priceTickX42);
event UniswapFeeTierAdded(uint24 fee);
event UniswapOracleProbed(uint24 fee, uint136 avLiquidity, uint40 period, uint16 cardinalityToIncrease);
function TWAP_DURATION() external view returns (uint40);
function getPrice(address collateralToken, address debtToken) external view returns (int64);
function getUniswapFeeTiers() external view returns (SirStructs.UniswapFeeTier[] memory uniswapFeeTiers);
function initialize(address tokenA, address tokenB) external;
function newUniswapFeeTier(uint24 fee) external;
function state(address token0, address token1) external view returns (SirStructs.OracleState memory);
function uniswapFeeTierAddressOf(address tokenA, address tokenB) external view returns (address);
function uniswapFeeTierOf(address tokenA, address tokenB) external view returns (uint24);
function updateOracleState(
address collateralToken,
address debtToken
) external returns (int64 tickPriceX42, address uniswapPoolAddress);
}
"
},
"lib/Core/lib/v3-core/contracts/interfaces/IUniswapV3Pool.sol": {
"content": "// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
import {IUniswapV3PoolImmutables} from './pool/IUniswapV3PoolImmutables.sol';
import {IUniswapV3PoolState} from './pool/IUniswapV3PoolState.sol';
import {IUniswapV3PoolDerivedState} from './pool/IUniswapV3PoolDerivedState.sol';
import {IUniswapV3PoolActions} from './pool/IUniswapV3PoolActions.sol';
import {IUniswapV3PoolOwnerActions} from './pool/IUniswapV3PoolOwnerActions.sol';
import {IUniswapV3PoolErrors} from './pool/IUniswapV3PoolErrors.sol';
import {IUniswapV3PoolEvents} from './pool/IUniswapV3PoolEvents.sol';
/// @title The interface for a Uniswap V3 Pool
/// @notice A Uniswap pool facilitates swapping and automated market making between any two assets that strictly conform
/// to the ERC20 specification
/// @dev The pool interface is broken up into many smaller pieces
interface IUniswapV3Pool is
IUniswapV3PoolImmutables,
IUniswapV3PoolState,
IUniswapV3PoolDerivedState,
IUniswapV3PoolActions,
IUniswapV3PoolOwnerActions,
IUniswapV3PoolErrors,
IUniswapV3PoolEvents
{
}
"
},
"lib/Core/src/libraries/SirStructs.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
library SirStructs {
struct VaultIssuanceParams {
uint8 tax; // (tax / type(uint8).max * 10%) of its fee revenue is directed to the Treasury.
uint40 timestampLastUpdate; // timestamp of the last time cumulativeSIRPerTEAx96 was updated. 0 => use systemParams.timestampIssuanceStart instead
uint176 cumulativeSIRPerTEAx96; // Q104.96, cumulative SIR minted by the vaultId per unit of TEA.
}
struct VaultParameters {
address debtToken;
address collateralToken;
int8 leverageTier;
}
struct FeeStructure {
uint16 fee; // Fee in basis points.
uint16 feeNew; // New fee to replace fee if current time exceeds FEE_CHANGE_DELAY since timestampUpdate
uint40 timestampUpdate; // Timestamp fee change was made. If 0, feeNew is not used.
}
struct SystemParameters {
FeeStructure baseFee;
FeeStructure lpFee;
bool mintingStopped; // If true, no minting of TEA/APE
/** Aggregated taxes for all vaults. Choice of uint16 type.
For vault i, (tax_i / type(uint8).max)*10% is charged, where tax_i is of type uint8.
They must satisfy the condition
Σ_i (tax_i / type(uint8).max)^2 ≤ 0.1^2
Under this constraint, cumulativeTax = Σ_i tax_i is maximized when all taxes are equal (tax_i = tax for all i) and
tax = type(uint8).max / sqrt(Nvaults)
Since the lowest non-zero value is tax=1, the maximum number of vaults with non-zero tax is
Nvaults = type(uint8).max^2 < type(uint16).max
*/
uint16 cumulativeTax;
}
/** Collateral owned by the apes and LPers in a vault
*/
struct Reserves {
uint144 reserveApes;
uint144 reserveLPers;
int64 tickPriceX42;
}
/** Data needed for recoverying the amount of collateral owned by the apes and LPers in a vault
*/
struct VaultState {
uint144 reserve; // reserve = reserveApes + reserveLPers
/** Price at the border of the power and saturation zone.
Q21.42 - Fixed point number with 42 bits of precision after the comma.
type(int64).max and type(int64).min are used to represent +∞ and -∞ respectively.
*/
int64 tickPriceSatX42; // Saturation price in Q21.42 fixed point
uint48 vaultId; // Allows the creation of approximately 281 trillion vaults
}
/** The sum of all amounts in Fees are equal to the amounts deposited by the user (in the case of a mint)
or taken out by the user (in the case of a burn).
collateralInOrWithdrawn: Amount of collateral deposited by the user (in the case of a mint) or taken out by the user (in the case of a burn).
collateralFeeToStakers: Amount of collateral paid to the stakers.
collateralFeeToLPers: Amount of collateral paid to the gentlemen.
collateralFeeToProtocol: Amount of collateral paid to the protocol.
*/
struct Fees {
uint144 collateralInOrWithdrawn;
uint144 collateralFeeToStakers;
uint144 collateralFeeToLPers; // Sometimes all LPers and sometimes only protocol owned liquidity
}
struct StakingParams {
uint80 stake; // Amount of staked SIR
uint176 cumulativeETHPerSIRx80; // Cumulative ETH per SIR * 2^80
}
struct StakerParams {
uint80 stake; // Total amount of staked SIR by the staker
uint176 cumulativeETHPerSIRx80; // Cumulative ETH per SIR * 2^80 last time the user updated his balance of ETH dividends
uint80 lockedStake; // Amount of stake that was locked at time 'tsLastUpdate'
uint40 tsLastUpdate; // Timestamp of the last time the user staked or unstaked
}
struct Auction {
address bidder; // Address of the bidder
uint96 bid; // Amount of the bid
uint40 startTime; // Auction start time
}
struct OracleState {
int64 tickPriceX42; // Last stored price. Q21.42
uint40 timeStampPrice; // Timestamp of the last stored price
uint8 indexFeeTier; // Uniswap v3 fee tier currently being used as oracle
uint8 indexFeeTierProbeNext; // Uniswap v3 fee tier to probe next
uint40 timeStampFeeTier; // Timestamp of the last probed fee tier
bool initialized; // Whether the oracle has been initialized
UniswapFeeTier uniswapFeeTier; // Uniswap v3 fee tier currently being used as oracle
}
/**
* Parameters of a Uniswap v3 tier.
*/
struct UniswapFeeTier {
uint24 fee;
int24 tickSpacing;
}
}
"
},
"lib/Core/src/libraries/SystemConstants.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
library SystemConstants {
uint8 internal constant SIR_DECIMALS = 12;
/** SIR Token Issuance Rate
If we want to issue 2,015,000,000 SIR per year, this implies an issuance rate of 63.9 SIR/s.
*/
uint72 internal constant ISSUANCE = uint72(2015e6 * 10 ** SIR_DECIMALS - 1) / 365 days + 1; // [sir/s]
/** During the first 3 years, 30%-to-33% of the emissions are diverged to contributors.
- 10% to pre-mainnet contributors
- 10%-13% to fundraising contributors
- 10% to a treasury for post-mainnet stuff
*/
uint72 internal constant LP_ISSUANCE_FIRST_3_YEARS = uint72((uint256(56048532400000000) * ISSUANCE) / 1e17);
uint128 internal constant TEA_MAX_SUPPLY = (uint128(LP_ISSUANCE_FIRST_3_YEARS) << 96) / type(uint16).max; // Must fit in uint128
uint40 internal constant THREE_YEARS = 3 * 365 days;
int64 internal constant MAX_TICK_X42 = 1951133415219145403; // log_1.0001(x)*2^42 where x is the max possible Q64.64 value, i.e., 2^64 - 2^-64
// Approximately 10 days. We did not choose 10 days precisely to avoid auctions always ending on the same day and time of the week.
uint40 internal constant AUCTION_COOLDOWN = 247 hours; // 247h & 240h have no common factors
// Duration of an auction
uint40 internal constant AUCTION_DURATION = 24 hours;
// Time it takes for a change of LP or base fee to take effect
uint256 internal constant FEE_CHANGE_DELAY = 10 days;
uint40 internal constant SHUTDOWN_WITHDRAWAL_DELAY = 20 days;
int8 internal constant MAX_LEVERAGE_TIER = 2;
int8 internal constant MIN_LEVERAGE_TIER = -4;
uint256 internal constant HALVING_PERIOD = 30 days; // Every 30 days, half of the locked stake is unlocked
}
"
},
"lib/Core/src/libraries/FullMath.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @title Contains 512-bit math functions
/// @notice Facilitates multiplication and division that can have overflow of an intermediate value without any loss of precision
/// @dev Handles "phantom overflow" i.e., allows multiplication and division where an intermediate value overflows 256 bits
/// @dev Modified from https://github.com/Uniswap/v3-core/blob/main/contracts/libraries/FullMath.sol so that it can compile on Solidity 8
library FullMath {
/// @notice Calculates floor(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
/// @param a The multiplicand
/// @param b The multiplier
/// @param denominator The divisor
/// @return result The 256-bit result
/// @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv
function mulDiv(uint256 a, uint256 b, uint256 denominator) internal pure returns (uint256 result) {
// 512-bit multiply [prod1 prod0] = a * b
// Compute the product mod 2**256 and mod 2**256 - 1
// then use the Chinese Remainder Theorem to reconstruct
// the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2**256 + prod0
uint256 prod0; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(a, b, not(0))
prod0 := mul(a, b)
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division
if (prod1 == 0) {
require(denominator > 0);
assembly {
result := div(prod0, denominator)
}
return result;
}
// Make sure the result is less than 2**256.
// Also prevents denominator == 0
require(denominator > prod1);
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0]
// Compute remainder using mulmod
uint256 remainder;
assembly {
remainder := mulmod(a, b, denominator)
}
// Subtract 256 bit number from 512 bit number
assembly {
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator
// Compute largest power of two divisor of denominator.
// Always >= 1.
unchecked {
uint256 twos = (type(uint256).max - denominator + 1) & denominator;
// Divide denominator by power of two
assembly {
denominator := div(denominator, twos)
}
// Divide [prod1 prod0] by the factors of two
assembly {
prod0 := div(prod0, twos)
}
// Shift in bits from prod1 into prod0. For this we need
// to flip `twos` such that it is 2**256 / twos.
// If twos is zero, then it becomes one
assembly {
twos := add(div(sub(0, twos), twos), 1)
}
prod0 |= prod1 * twos;
// Invert denominator mod 2**256
// Now that denominator is an odd number, it has an inverse
// modulo 2**256 such that denominator * inv = 1 mod 2**256.
// Compute the inverse by starting with a seed that is correct
// correct for four bits. That is, denominator * inv = 1 mod 2**4
uint256 inv = (3 * denominator) ^ 2;
// Now use Newton-Raphson iteration to improve the precision.
// Thanks to Hensel's lifting lemma, this also works in modular
// arithmetic, doubling the correct bits in each step.
inv *= 2 - denominator * inv; // inverse mod 2**8
inv *= 2 - denominator * inv; // inverse mod 2**16
inv *= 2 - denominator * inv; // inverse mod 2**32
inv *= 2 - denominator * inv; // inverse mod 2**64
inv *= 2 - denominator * inv; // inverse mod 2**128
inv *= 2 - denominator * inv; // inverse mod 2**256
// Because the division is now exact we can divide by multiplying
// with the modular inverse of denominator. This will give us the
// correct result modulo 2**256. Since the precoditions guarantee
// that the outcome is less than 2**256, this is the final result.
// We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inv;
return result;
}
}
function tryMulDiv(uint256 a, uint256 b, uint256 denominator) internal pure returns (bool success, uint256 result) {
// 512-bit multiply [prod1 prod0] = a * b
// Compute the product mod 2**256 and mod 2**256 - 1
// then use the Chinese Remainder Theorem to reconstruct
// the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2**256 + prod0
uint256 prod0; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(a, b, not(0))
prod0 := mul(a, b)
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division
if (prod1 == 0) {
if (denominator == 0) return (false, 0);
assembly {
result := div(prod0, denominator)
}
return (true, result);
}
// Make sure the result is less than 2**256.
// Also prevents denominator == 0
if (denominator <= prod1) return (false, 0);
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0]
// Compute remainder using mulmod
uint256 remainder;
assembly {
remainder := mulmod(a, b, denominator)
}
// Subtract 256 bit number from 512 bit number
assembly {
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator
// Compute largest power of two divisor of denominator.
// Always >= 1.
unchecked {
uint256 twos = (type(uint256).max - denominator + 1) & denominator;
// Divide denominator by power of two
assembly {
denominator := div(denominator, twos)
}
// Divide [prod1 prod0] by the factors of two
assembly {
prod0 := div(prod0, twos)
}
// Shift in bits from prod1 into prod0. For this we need
// to flip `twos` such that it is 2**256 / twos.
// If twos is zero, then it becomes one
assembly {
twos := add(div(sub(0, twos), twos), 1)
}
prod0 |= prod1 * twos;
// Invert denominator mod 2**256
// Now that denominator is an odd number, it has an inverse
// modulo 2**256 such that denominator * inv = 1 mod 2**256.
// Compute the inverse by starting with a seed that is correct
// correct for four bits. That is, denominator * inv = 1 mod 2**4
uint256 inv = (3 * denominator) ^ 2;
// Now use Newton-Raphson iteration to improve the precision.
// Thanks to Hensel's lifting lemma, this also works in modular
// arithmetic, doubling the correct bits in each step.
inv *= 2 - denominator * inv; // inverse mod 2**8
inv *= 2 - denominator * inv; // inverse mod 2**16
inv *= 2 - denominator * inv; // inverse mod 2**32
inv *= 2 - denominator * inv; // inverse mod 2**64
inv *= 2 - denominator * inv; // inverse mod 2**128
inv *= 2 - denominator * inv; // inverse mod 2**256
// Because the division is now exact we can divide by multiplying
// with the modular inverse of denominator. This will give us the
// correct result modulo 2**256. Since the precoditions guarantee
// that the outcome is less than 2**256, this is the final result.
// We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inv;
return (true, result);
}
}
/// @notice Calculates ceil(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
/// @param a The multiplicand
/// @param b The multiplier
/// @param denominator The divisor
/// @return result The 256-bit result
function mulDivRoundingUp(uint256 a, uint256 b, uint256 denominator) internal pure returns (uint256 result) {
result = mulDiv(a, b, denominator);
if (mulmod(a, b, denominator) > 0) {
require(result < type(uint256).max);
result++;
}
}
function tryMulDivRoundingUp(
uint256 a,
uint256 b,
uint256 denominator
) internal pure returns (bool success, uint256 result) {
(success, result) = tryMulDiv(a, b, denominator);
if (success && mulmod(a, b, denominator) > 0) {
if (result == type(uint256).max) return (false, 0);
result++;
}
}
}
"
},
"lib/Core/src/interfaces/IWETH9.sol": {
"content": "// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
/// @title Interface for WETH9
interface IWETH9 is IERC20 {
/// @notice Deposit ether to get wrapped ether
function deposit() external payable;
/// @notice Withdraw wrapped ether to get ether
function withdraw(uint256) external;
}
"
},
"lib/Core/src/libraries/UniswapPoolAddress.sol": {
"content": "// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
/// @title Provides functions for deriving a pool address from the factory, tokens, and the fee
/// @notice Modified from https://github.com/Uniswap/v3-periphery/blob/main/contracts/libraries/PoolAddress.sol
library UniswapPoolAddress {
bytes32 internal constant POOL_INIT_CODE_HASH = 0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54;
/// @notice The identifying key of the pool
struct PoolKey {
address token0;
address token1;
uint24 fee;
}
/// @notice Returns PoolKey: the ordered tokens with the matched fee levels
/// @param tokenA The first token of a pool, unsorted
/// @param tokenB The second token of a pool, unsorted
/// @param fee The fee level of the pool
/// @return Poolkey The pool details with ordered token0 and token1 assignments
function getPoolKey(address tokenA, address tokenB, uint24 fee) internal pure returns (PoolKey memory) {
if (tokenA > tokenB) (tokenA, tokenB) = (tokenB, tokenA);
return PoolKey({token0: tokenA, token1: tokenB, fee: fee});
}
/// @notice Deterministically computes the pool address given the factory and PoolKey
/// @param factory The Uniswap V3 factory contract address
/// @param key The PoolKey
/// @return pool The contract address of the V3 pool
function computeAddress(address factory, PoolKey memory key) internal pure returns (address pool) {
require(key.token0 < key.token1);
pool = address(
uint160(
uint256(
keccak256(
abi.encodePacked(
hex"ff",
factory,
keccak256(abi.encode(key.token0, key.token1, key.fee)),
POOL_INIT_CODE_HASH
)
)
)
)
);
}
}
"
},
"lib/Core/src/libraries/AddressClone.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
library AddressClone {
/// @dev Hash of the `_CREATE3_PROXY_BYTECODE`.
/// Equivalent to `keccak256(abi.encodePacked(hex"67363d3d37363d34f03d5260086018f3"))`.
bytes32 private constant _CREATE3_PROXY_BYTECODE_HASH =
0x21c35dbe1b344a2488cf3321d6ce542f8e9f305544ff09e4993a62319a497c1f;
function getAddress(address deployer, uint256 vaultId) internal pure returns (address clone) {
/// @solidity memory-safe-assembly
// solhint-disable-next-line no-inline-assembly
assembly {
// Cache the free memory pointer.
let m := mload(0x40)
// Store `address(this)`.
mstore(0x00, deployer)
// Store the prefix.
mstore8(0x0b, 0xff)
// Store the salt.
mstore(0x20, vaultId)
// Store the bytecode hash.
mstore(0x40, _CREATE3_PROXY_BYTECODE_HASH)
// Store the proxy's address.
mstore(0x14, keccak256(0x0b, 0x55))
// Restore the free memory pointer.
mstore(0x40, m)
// 0xd6 = 0xc0 (short RLP prefix) + 0x16 (length of: 0x94 ++ proxy ++ 0x01).
// 0x94 = 0x80 + 0x14 (0x14 = the length of an address, 20 bytes, in hex).
mstore(0x00, 0xd694)
// Nonce of the proxy contract (1).
mstore8(0x34, 0x01)
clone := and(keccak256(0x1e, 0x17), 0xffffffffffffffffffffffffffffffffffffffff)
}
}
}
"
},
"lib/Core/lib/v3-core/contracts/libraries/TickMath.sol": {
"content": "// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
/// @title Math library for computing sqrt prices from ticks and vice versa
/// @notice Computes sqrt price for ticks of size 1.0001, i.e. sqrt(1.0001^tick) as fixed point Q64.96 numbers. Supports
/// prices between 2**-128 and 2**128
library TickMath {
error T();
error R();
/// @dev The minimum tick that may be passed to #getSqrtRatioAtTick computed from log base 1.0001 of 2**-128
int24 internal constant MIN_TICK = -887272;
/// @dev The maximum tick that may be passed to #getSqrtRatioAtTick computed from log base 1.0001 of 2**128
int24 internal constant MAX_TICK = -MIN_TICK;
/// @dev The minimum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MIN_TICK)
uint160 internal constant MIN_SQRT_RATIO = 4295128739;
/// @dev The maximum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MAX_TICK)
uint160 internal constant MAX_SQRT_RATIO = 1461446703485210103287273052203988822378723970342;
/// @notice Calculates sqrt(1.0001^tick) * 2^96
/// @dev Throws if |tick| > max tick
/// @param tick The input tick for the above formula
/// @return sqrtPriceX96 A Fixed point Q64.96 number representing the sqrt of the ratio of the two assets (token1/token0)
/// at the given tick
function getSqrtRatioAtTick(int24 tick) internal pure returns (uint160 sqrtPriceX96) {
unchecked {
uint256 absTick = tick < 0 ? uint256(-int256(tick)) : uint256(int256(tick));
if (absTick > uint256(int256(MAX_TICK))) revert T();
uint256 ratio = absTick & 0x1 != 0
? 0xfffcb933bd6fad37aa2d162d1a594001
: 0x100000000000000000000000000000000;
if (absTick & 0x2 != 0) ratio = (ratio * 0xfff97272373d413259a46990580e213a) >> 128;
if (absTick & 0x4 != 0) ratio = (ratio * 0xfff2e50f5f656932ef12357cf3c7fdcc) >> 128;
if (absTick & 0x8 != 0) ratio = (ratio * 0xffe5caca7e10e4e61c3624eaa0941cd0) >> 128;
if (absTick & 0x10 != 0) ratio = (ratio * 0xffcb9843d60f6159c9db58835c926644) >> 128;
if (absTick & 0x20 != 0) ratio = (ratio * 0xff973b41fa98c081472e6896dfb254c0) >> 128;
if (absTick & 0x40 != 0) ratio = (ratio * 0xff2ea16466c96a3843ec78b326b52861) >> 128;
if (absTick & 0x80 != 0) ratio = (ratio * 0xfe5dee046a99a2a811c461f1969c3053) >> 128;
if (absTick & 0x100 != 0) ratio = (ratio * 0xfcbe86c7900a88aedcffc83b479aa3a4) >> 128;
if (absTick & 0x200 != 0) ratio = (ratio * 0xf987a7253ac413176f2b074cf7815e54) >> 128;
if (absTick & 0x400 != 0) ratio = (ratio * 0xf3392b0822b70005940c7a398e4b70f3) >> 128;
if (absTick & 0x800 != 0) ratio = (ratio * 0xe7159475a2c29b7443b29c7fa6e889d9) >> 128;
if (absTick & 0x1000 != 0) ratio = (ratio * 0xd097f3bdfd2022b8845ad8f792aa5825) >> 128;
if (absTick & 0x2000 != 0) ratio = (ratio * 0xa9f746462d870fdf8a65dc1f90e061e5) >> 128;
if (absTick & 0x4000 != 0) ratio = (ratio * 0x70d869a156d2a1b890bb3df62baf32f7) >> 128;
if (absTick & 0x8000 != 0) ratio = (ratio * 0x31be135f97d08fd981231505542fcfa6) >> 128;
if (absTick & 0x10000 != 0) ratio = (ratio * 0x9aa508b5b7a84e1c677de54f3e99bc9) >> 128;
if (absTick & 0x20000 != 0) ratio = (ratio * 0x5d6af8dedb81196699c329225ee604) >> 128;
if (absTick & 0x40000 != 0) ratio = (ratio * 0x2216e584f5fa1ea926041bedfe98) >> 128;
if (absTick & 0x80000 != 0) ratio = (ratio * 0x48a170391f7dc42444e8fa2) >> 128;
if (tick > 0) ratio = type(uint256).max / ratio;
// this divides by 1<<32 rounding up to go from a Q128.128 to a Q128.96.
// we then downcast because we know the result always fits within 160 bits due to our tick input constraint
// we round up in the division so getTickAtSqrtRatio of the output price is always consistent
sqrtPriceX96 = uint160((ratio >> 32) + (ratio % (1 << 32) == 0 ? 0 : 1));
}
}
/// @notice Calculates the greatest tick value such that getRatioAtTick(tick) <= ratio
/// @dev Throws in case sqrtPriceX96 < MIN_SQRT_RATIO, as MIN_SQRT_RATIO is the lowest value getRatioAtTick may
/// ever return.
/// @param sqrtPriceX96 The sqrt ratio for which to compute the tick as a Q64.96
/// @return tick The greatest tick for which the ratio is less than or equal to the input ratio
function getTickAtSqrtRatio(uint160 sqrtPriceX96) internal pure returns (int24 tick) {
unchecked {
// second inequality must be < because the price can never reach the price at the max tick
if (!(sqrtPriceX96 >= MIN_SQRT_RATIO && sqrtPriceX96 < MAX_SQRT_RATIO)) revert R();
uint256 ratio = uint256(sqrtPriceX96) << 32;
uint256 r = ratio;
uint256 msb = 0;
assembly {
let f := shl(7, gt(r, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF))
msb := or(msb, f)
r := shr(f, r)
}
assembly {
let f := shl(6, gt(r, 0xFFFFFFFFFFFFFFFF))
msb := or(msb, f)
r := shr(f, r)
}
Submitted on: 2025-10-07 11:50:29
Comments
Log in to comment.
No comments yet.