Source Code
Overview
ETH Balance
0 ETH
ETH Value
$0.00
Cross-Chain Transactions
Loading...
Loading
Contract Name:
CurveLiqArbitrage
Compiler Version
v0.8.20+commit.a1b79de6
Optimization Enabled:
Yes with 200 runs
Other Settings:
shanghai EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
pragma solidity 0.8.20;
import {Math} from "openzeppelin-math/Math.sol";
import {IERC20Metadata} from "openzeppelin-contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {ICurvePool} from "../../interfaces/ICurvePool.sol";
import {ICurveNGPool} from "../../interfaces/ICurveNGPool.sol";
import {Constants} from "../Constants.sol";
contract CurveLiqArbitrage {
using Math for uint256;
// Constants
uint256 public constant CURVE_UNIT = 1e18;
uint256 public constant PROP_MIN = 1e16;
uint256 public constant PROP_MAX = 1e20;
uint256 public constant MAX_ITERS = 255;
uint256 public constant INV_PHI = 618033988749894848;
// Errors
error ConvergenceError(
uint256 propMin,
uint256 propMax,
uint256 epsilonTheory,
uint256 epsilonActual
);
/**
* @dev Previews the rate of LP tokens obtained per unit of token0, when depositing at a given proportion.
* @param curvePool Address of curve pool
* @param depositInToken0 Total deposit denominated in token 0.
* @param proportion proportion at which we want to add liquidity: numberIBTs/numberPTs. In 18 decimals.
* @return Amount of Curve LP tokens minted per unit of token0 deposited at a given proportion of token0 and token1.
*/
function previewUnitaryAddLiquidity(
address curvePool,
uint256 depositInToken0,
uint256 proportion
) public view returns (uint256) {
// Constraints: amountToken0 + last_prices * amountToken1 = depositInToken0
// and amountToken0/amountToken1 = proportion
// Hence: amountToken0 = amountToken1 * proportion
// and amountToken1 = depositInToken0/(proportion + last_prices)
uint256 amountToken1 = depositInToken0.mulDiv(
CURVE_UNIT,
proportion + ICurvePool(curvePool).last_prices()
);
uint256 amountToken0 = amountToken1.mulDiv(proportion, CURVE_UNIT);
uint256 tokenUnit = 10 ** IERC20Metadata(ICurvePool(curvePool).coins(0)).decimals();
return
ICurvePool(curvePool).calc_token_amount([amountToken0, amountToken1]).mulDiv(
tokenUnit,
depositInToken0
);
}
/**
* @dev Adaptation of above method to support Curve NG pools
* @dev Previews the rate of LP tokens obtained per unit of token0, when depositing at a given proportion.
* @param curvePool Address of curve pool
* @param depositInToken0 Total deposit denominated in token 0.
* @param proportion proportion at which we want to add liquidity: numberIBTs/numberPTs. In 18 decimals.
* @return Amount of Curve LP tokens minted per unit of token0 deposited at a given proportion of token0 and token1.
*/
function previewNGUnitaryAddLiquidity(
address curvePool,
uint256 depositInToken0,
uint256 proportion
) public view returns (uint256) {
// Constraints: amountToken0 + last_prices * amountToken1 = depositInToken0
// and amountToken0/amountToken1 = proportion
// Hence: amountToken0 = amountToken1 * proportion
// and amountToken1 = depositInToken0/(proportion + last_prices)
uint256 amountToken1 = depositInToken0.mulDiv(
CURVE_UNIT,
proportion + ICurveNGPool(curvePool).last_prices()
);
uint256 amountToken0 = amountToken1.mulDiv(proportion, CURVE_UNIT);
uint256 tokenUnit = 10 ** IERC20Metadata(ICurveNGPool(curvePool).coins(0)).decimals();
return
ICurveNGPool(curvePool).calc_token_amount([amountToken0, amountToken1], true).mulDiv(
tokenUnit,
depositInToken0
);
}
/**
* @dev Searches for the proportion that maximizes a liquidity deposit of value depositInToken0, using golden section
* search. Concretely, it maximizes the amount of LP tokens received.
* Golden Section Search reference: https://en.wikipedia.org/wiki/Golden-section_search
* @param curvePool Address of curve pool
* @param depositInToken0 Total deposit denominated in token0.
* @param epsilon Error tolerance (18 decimals)
* @return Proportion that maximizes the amount of LP tokens minted.
*/
function findBestProportion(
address curvePool,
uint256 depositInToken0,
uint256 epsilon
) public view returns (uint256) {
uint256 propMin = PROP_MIN;
uint256 propMax = PROP_MAX;
uint256 m1 = 0;
uint256 m2 = 0;
uint256 iters = 0;
uint256 lpRate1 = 0;
uint256 lpRate2 = 0;
while (propMax - propMin > epsilon) {
if (iters > MAX_ITERS) {
revert ConvergenceError(propMin, propMax, epsilon, propMax - propMin);
}
m1 = propMax - (propMax - propMin).mulDiv(INV_PHI, CURVE_UNIT);
m2 = propMin + (propMax - propMin).mulDiv(INV_PHI, CURVE_UNIT);
lpRate1 = previewUnitaryAddLiquidity(curvePool, depositInToken0, m1);
lpRate2 = previewUnitaryAddLiquidity(curvePool, depositInToken0, m2);
if (lpRate1 > lpRate2) {
propMax = m2;
} else {
propMin = m1;
}
++iters;
}
return (propMin + propMax) / 2;
}
/**
* @dev Adaptation of the above method to support Curve NG pools
* @dev Searches for the proportion that maximizes a liquidity deposit of value depositInToken0, using golden section
* search. Concretely, it maximizes the amount of LP tokens received.
* Golden Section Search reference: https://en.wikipedia.org/wiki/Golden-section_search
* @param curvePool Address of curve pool
* @param depositInToken0 Total deposit denominated in token0.
* @param epsilon Error tolerance (18 decimals)
* @return Proportion that maximizes the amount of LP tokens minted.
*/
function findBestProportionNG(
address curvePool,
uint256 depositInToken0,
uint256 epsilon
) public view returns (uint256) {
uint256 propMin = PROP_MIN;
uint256 propMax = PROP_MAX;
uint256 m1 = 0;
uint256 m2 = 0;
uint256 iters = 0;
uint256 lpRate1 = 0;
uint256 lpRate2 = 0;
while (propMax - propMin > epsilon) {
if (iters > MAX_ITERS) {
revert ConvergenceError(propMin, propMax, epsilon, propMax - propMin);
}
m1 = propMax - (propMax - propMin).mulDiv(INV_PHI, CURVE_UNIT);
m2 = propMin + (propMax - propMin).mulDiv(INV_PHI, CURVE_UNIT);
lpRate1 = previewNGUnitaryAddLiquidity(curvePool, depositInToken0, m1);
lpRate2 = previewNGUnitaryAddLiquidity(curvePool, depositInToken0, m2);
if (lpRate1 > lpRate2) {
propMax = m2;
} else {
propMin = m1;
}
++iters;
}
return (propMin + propMax) / 2;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 value) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 value) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Metadata.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
/**
* @dev Interface for the optional metadata functions from the ERC20 standard.
*/
interface IERC20Metadata is IERC20 {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
/**
* @dev Muldiv operation overflow.
*/
error MathOverflowedMulDiv();
enum Rounding {
Floor, // Toward negative infinity
Ceil, // Toward positive infinity
Trunc, // Toward zero
Expand // Away from zero
}
/**
* @dev Returns the addition of two unsigned integers, with an overflow flag.
*/
function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the subtraction of two unsigned integers, with an overflow flag.
*/
function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b > a) return (false, 0);
return (true, a - b);
}
}
/**
* @dev Returns the multiplication of two unsigned integers, with an overflow flag.
*/
function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the division of two unsigned integers, with a division by zero flag.
*/
function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a / b);
}
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
*/
function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a % b);
}
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds towards infinity instead
* of rounding towards zero.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
if (b == 0) {
// Guarantee the same behavior as in a regular Solidity division.
return a / b;
}
// (a + b - 1) / b can overflow on addition, so we distribute.
return a == 0 ? 0 : (a - 1) / b + 1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
* denominator == 0.
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
* Uniswap Labs also under MIT license.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2^256 + prod0.
uint256 prod0 = x * y; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
// Solidity will revert if denominator == 0, unlike the div opcode on its own.
// The surrounding unchecked block does not change this fact.
// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
if (denominator <= prod1) {
revert MathOverflowedMulDiv();
}
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator.
// Always >= 1. See https://cs.stackexchange.com/q/138556/92363.
uint256 twos = denominator & (0 - denominator);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 2^4.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also
// works in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // inverse mod 2^256
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
* towards zero.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
//
// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
//
// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
// ? `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
// ? `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
//
// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
uint256 result = 1 << (log2(a) >> 1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
// into the expected uint128 result.
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256 of a positive value rounded towards zero.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 256, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);
}
}
/**
* @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
*/
function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
return uint8(rounding) % 2 == 1;
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.20;
import {IERC20Metadata} from "openzeppelin-contracts/token/ERC20/extensions/IERC20Metadata.sol";
/**
* @dev Interface for Curve TwoCrypto-NG pool
*/
interface ICurveNGPool is IERC20Metadata {
function coins(uint256 index) external view returns (address);
function balances(uint256 index) external view returns (uint256);
function A() external view returns (uint256);
function gamma() external view returns (uint256);
function D() external view returns (uint256);
function token() external view returns (address);
function price_scale() external view returns (uint256);
function price_oracle() external view returns (uint256);
function future_A_gamma_time() external view returns (uint256);
function future_A_gamma() external view returns (uint256);
function initial_A_gamma_time() external view returns (uint256);
function initial_A_gamma() external view returns (uint256);
function fee_gamma() external view returns (uint256);
function mid_fee() external view returns (uint256);
function out_fee() external view returns (uint256);
function allowed_extra_profit() external view returns (uint256);
function adjustment_step() external view returns (uint256);
function admin_fee() external view returns (uint256);
function ma_time() external view returns (uint256);
function get_virtual_price() external view returns (uint256);
function fee() external view returns (uint256);
function get_dy(uint256 i, uint256 j, uint256 dx) external view returns (uint256);
function get_dx(uint256 i, uint256 j, uint256 dy) external view returns (uint256);
function last_prices() external view returns (uint256);
function calc_token_amount(
uint256[2] calldata amounts,
bool deposit
) external view returns (uint256);
function calc_withdraw_one_coin(
uint256 _token_amount,
uint256 i
) external view returns (uint256);
function exchange(uint256 i, uint256 j, uint256 dx, uint256 min_dy) external returns (uint256);
function exchange(
uint256 i,
uint256 j,
uint256 dx,
uint256 min_dy,
address receiver
) external returns (uint256);
function add_liquidity(
uint256[2] calldata amounts,
uint256 min_mint_amount
) external returns (uint256);
function add_liquidity(
uint256[2] calldata amounts,
uint256 min_mint_amount,
address receiver
) external returns (uint256);
function remove_liquidity(uint256 amount, uint256[2] calldata min_amounts) external;
function remove_liquidity(
uint256 amount,
uint256[2] calldata min_amounts,
address receiver
) external;
function remove_liquidity_one_coin(
uint256 token_amount,
uint256 i,
uint256 min_amount
) external;
function remove_liquidity_one_coin(
uint256 token_amount,
uint256 i,
uint256 min_amount,
address receiver
) external;
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.20;
/**
* @dev Interface for Curve CryptoSwap pool
*/
interface ICurvePool {
function coins(uint256 index) external view returns (address);
function balances(uint256 index) external view returns (uint256);
function A() external view returns (uint256);
function gamma() external view returns (uint256);
function D() external view returns (uint256);
function token() external view returns (address);
function price_scale() external view returns (uint256);
function future_A_gamma_time() external view returns (uint256);
function future_A_gamma() external view returns (uint256);
function initial_A_gamma_time() external view returns (uint256);
function initial_A_gamma() external view returns (uint256);
function fee_gamma() external view returns (uint256);
function mid_fee() external view returns (uint256);
function out_fee() external view returns (uint256);
function allowed_extra_profit() external view returns (uint256);
function adjustment_step() external view returns (uint256);
function admin_fee() external view returns (uint256);
function ma_half_time() external view returns (uint256);
function get_virtual_price() external view returns (uint256);
function fee() external view returns (uint256);
function get_dy(uint256 i, uint256 j, uint256 dx) external view returns (uint256);
function last_prices() external view returns (uint256);
function calc_token_amount(uint256[2] calldata amounts) external view returns (uint256);
function calc_withdraw_one_coin(
uint256 _token_amount,
uint256 i
) external view returns (uint256);
function exchange(
uint256 i,
uint256 j,
uint256 dx,
uint256 min_dy,
bool use_eth,
address receiver
) external returns (uint256);
function add_liquidity(
uint256[2] calldata amounts,
uint256 min_mint_amount
) external returns (uint256);
function add_liquidity(
uint256[2] calldata amounts,
uint256 min_mint_amount,
bool use_eth,
address receiver
) external returns (uint256);
function remove_liquidity(uint256 amount, uint256[2] calldata min_amounts) external;
function remove_liquidity(
uint256 amount,
uint256[2] calldata min_amounts,
bool use_eth,
address receiver
) external;
function remove_liquidity_one_coin(
uint256 token_amount,
uint256 i,
uint256 min_amount
) external;
function remove_liquidity_one_coin(
uint256 token_amount,
uint256 i,
uint256 min_amount,
bool use_eth,
address receiver
) external;
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.20;
library Constants {
/// @dev 18 decimal unit
uint256 internal constant UNIT = 1e18;
/// @dev identifier for native ETH
address public constant ETH = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
/// @dev maximal number of iterations in the secant method algorithm
uint256 internal constant MAX_ITERATIONS_SECANT = 255;
/// @dev maximal number of iterations in the linear search following secant method algorithm
uint256 internal constant MAX_ITERATIONS_LINEAR_SEARCH = 255;
/// @dev determines the rate at which an input value is scaled in each iteration of linear search
uint256 internal constant SCALING_FACTOR_LINEAR_SEARCH = 1e6;
/// @dev precision divisor for the secant method
uint256 internal constant PRECISION_DIVISOR = 1000;
/// @dev Used for identifying cases when this contract's balance of a token is to be used as an input
/// This value is equivalent to 1<<255, i.e. a singular 1 in the most significant bit.
uint256 internal constant CONTRACT_BALANCE =
0x8000000000000000000000000000000000000000000000000000000000000000;
/// @dev Used as a flag for identifying that msg.sender should be used, saves gas by sending more 0 bytes
address internal constant MSG_SENDER = address(0xc0);
/// @dev Used as a flag for identifying address(this) should be used, saves gas by sending more 0 bytes
address internal constant ADDRESS_THIS = address(0xe0);
}{
"evmVersion": "shanghai",
"libraries": {},
"metadata": {
"appendCBOR": true,
"bytecodeHash": "ipfs",
"useLiteralContent": false
},
"optimizer": {
"enabled": true,
"runs": 200
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"remappings": [
"ds-test/=lib/forge-std/lib/ds-test/src/",
"erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
"forge-std/=lib/forge-std/src/",
"openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
"openzeppelin-contracts/=lib/openzeppelin-contracts/contracts/",
"openzeppelin-erc20-basic/=lib/openzeppelin-contracts/contracts/token/ERC20/",
"openzeppelin-erc20-extensions/=lib/openzeppelin-contracts-upgradeable/contracts/token/ERC20/extensions/",
"openzeppelin-erc20/=lib/openzeppelin-contracts-upgradeable/contracts/token/ERC20/",
"openzeppelin-math/=lib/openzeppelin-contracts/contracts/utils/math/",
"openzeppelin-proxy/=lib/openzeppelin-contracts-upgradeable/contracts/proxy/utils/",
"openzeppelin-utils/=lib/openzeppelin-contracts/contracts/utils/",
"config/=lib/spectra-contracts-configs/script/",
"@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
"@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
"DiamondRouter/=lib/DiamondRouter/",
"halmos-cheatcodes/=lib/DiamondRouter/lib/openzeppelin-contracts-upgradeable/lib/halmos-cheatcodes/src/",
"solidity-stringutils/=lib/DiamondRouter/lib/solidity-stringutils/",
"spectra-contracts-configs/=lib/spectra-contracts-configs/"
],
"viaIR": false
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"uint256","name":"propMin","type":"uint256"},{"internalType":"uint256","name":"propMax","type":"uint256"},{"internalType":"uint256","name":"epsilonTheory","type":"uint256"},{"internalType":"uint256","name":"epsilonActual","type":"uint256"}],"name":"ConvergenceError","type":"error"},{"inputs":[],"name":"MathOverflowedMulDiv","type":"error"},{"inputs":[],"name":"CURVE_UNIT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"INV_PHI","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_ITERS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PROP_MAX","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PROP_MIN","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"curvePool","type":"address"},{"internalType":"uint256","name":"depositInToken0","type":"uint256"},{"internalType":"uint256","name":"epsilon","type":"uint256"}],"name":"findBestProportion","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"curvePool","type":"address"},{"internalType":"uint256","name":"depositInToken0","type":"uint256"},{"internalType":"uint256","name":"epsilon","type":"uint256"}],"name":"findBestProportionNG","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"curvePool","type":"address"},{"internalType":"uint256","name":"depositInToken0","type":"uint256"},{"internalType":"uint256","name":"proportion","type":"uint256"}],"name":"previewNGUnitaryAddLiquidity","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"curvePool","type":"address"},{"internalType":"uint256","name":"depositInToken0","type":"uint256"},{"internalType":"uint256","name":"proportion","type":"uint256"}],"name":"previewUnitaryAddLiquidity","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]Contract Creation Code
608060405234801561000f575f80fd5b50610a168061001d5f395ff3fe608060405234801561000f575f80fd5b5060043610610090575f3560e01c806381321f951161006357806381321f95146100eb5780639afc8dbc146100fe578063b9dc6bfa14610111578063f14dda6e14610119578063ff930a9e14610127575f80fd5b806303e370711461009457806355bf9990146100b95780636322c667146100cc57806378b9088d146100db575b5f80fd5b6100a76100a2366004610790565b610136565b60405190815260200160405180910390f35b6100a76100c7366004610790565b61033e565b6100a7670de0b6b3a764000081565b6100a768056bc75e2d6310000081565b6100a76100f9366004610790565b6104ba565b6100a761010c366004610790565b6105e6565b6100a760ff81565b6100a7662386f26fc1000081565b6100a7670893b2a3668e24c081565b5f806101b6670de0b6b3a7640000866001600160a01b031663c146bf946040518163ffffffff1660e01b8152600401602060405180830381865afa158015610180573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906101a491906107c2565b6101ae90866107ed565b8691906106ba565b90505f6101cc8285670de0b6b3a76400006106ba565b60405163c661065760e01b81525f600482018190529192506001600160a01b0388169063c661065790602401602060405180830381865afa158015610213573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906102379190610806565b6001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015610272573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906102969190610821565b6102a190600a610921565b90506103318187896001600160a01b031663ed8e84f360405180604001604052808881526020018981525060016040518363ffffffff1660e01b81526004016102eb929190610957565b602060405180830381865afa158015610306573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061032a91906107c2565b91906106ba565b93505050505b9392505050565b5f80610388670de0b6b3a7640000866001600160a01b031663c146bf946040518163ffffffff1660e01b8152600401602060405180830381865afa158015610180573d5f803e3d5ffd5b90505f61039e8285670de0b6b3a76400006106ba565b60405163c661065760e01b81525f600482018190529192506001600160a01b0388169063c661065790602401602060405180830381865afa1580156103e5573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906104099190610806565b6001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015610444573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906104689190610821565b61047390600a610921565b90506103318187896001600160a01b0316638d8ea7276040518060400160405280888152602001898152506040518263ffffffff1660e01b81526004016102eb9190610974565b5f662386f26fc1000068056bc75e2d6310000082808080805b886104de8888610982565b11156105c15760ff83111561052d5786868a6104fa8383610982565b604051631b024b2b60e21b8152600481019490945260248401929092526044830152606482015260840160405180910390fd5b61054c670893b2a3668e24c0670de0b6b3a764000061032a8a8a610982565b6105569087610982565b9450610577670893b2a3668e24c0670de0b6b3a764000061032a8a8a610982565b61058190886107ed565b935061058e8b8b8761033e565b915061059b8b8b8661033e565b9050808211156105ad578395506105b1565b8496505b6105ba83610995565b92506104d3565b60026105cd87896107ed565b6105d791906109c1565b9b9a5050505050505050505050565b5f662386f26fc1000068056bc75e2d6310000082808080805b8861060a8888610982565b11156105c15760ff8311156106265786868a6104fa8383610982565b610645670893b2a3668e24c0670de0b6b3a764000061032a8a8a610982565b61064f9087610982565b9450610670670893b2a3668e24c0670de0b6b3a764000061032a8a8a610982565b61067a90886107ed565b93506106878b8b87610136565b91506106948b8b86610136565b9050808211156106a6578395506106aa565b8496505b6106b383610995565b92506105ff565b5f838302815f1985870982811083820303915050805f036106ee578382816106e4576106e46109ad565b0492505050610337565b80841161070e5760405163227bc15360e01b815260040160405180910390fd5b5f848688095f868103871696879004966002600389028118808a02820302808a02820302808a02820302808a02820302808a02820302808a02909103029181900381900460010186841190950394909402919094039290920491909117919091029150509392505050565b6001600160a01b038116811461078d575f80fd5b50565b5f805f606084860312156107a2575f80fd5b83356107ad81610779565b95602085013595506040909401359392505050565b5f602082840312156107d2575f80fd5b5051919050565b634e487b7160e01b5f52601160045260245ffd5b80820180821115610800576108006107d9565b92915050565b5f60208284031215610816575f80fd5b815161033781610779565b5f60208284031215610831575f80fd5b815160ff81168114610337575f80fd5b600181815b8085111561087b57815f1904821115610861576108616107d9565b8085161561086e57918102915b93841c9390800290610846565b509250929050565b5f8261089157506001610800565b8161089d57505f610800565b81600181146108b357600281146108bd576108d9565b6001915050610800565b60ff8411156108ce576108ce6107d9565b50506001821b610800565b5060208310610133831016604e8410600b84101617156108fc575081810a610800565b6109068383610841565b805f1904821115610919576109196107d9565b029392505050565b5f61033760ff841683610883565b805f5b6002811015610951578151845260209384019390910190600101610932565b50505050565b60608101610965828561092f565b82151560408301529392505050565b60408101610800828461092f565b81810381811115610800576108006107d9565b5f600182016109a6576109a66107d9565b5060010190565b634e487b7160e01b5f52601260045260245ffd5b5f826109db57634e487b7160e01b5f52601260045260245ffd5b50049056fea2646970667358221220bef52e5e9362f14f1386fabc481752dbb11e478700c2e8ca03c835ae1729dea564736f6c63430008140033
Deployed Bytecode
0x608060405234801561000f575f80fd5b5060043610610090575f3560e01c806381321f951161006357806381321f95146100eb5780639afc8dbc146100fe578063b9dc6bfa14610111578063f14dda6e14610119578063ff930a9e14610127575f80fd5b806303e370711461009457806355bf9990146100b95780636322c667146100cc57806378b9088d146100db575b5f80fd5b6100a76100a2366004610790565b610136565b60405190815260200160405180910390f35b6100a76100c7366004610790565b61033e565b6100a7670de0b6b3a764000081565b6100a768056bc75e2d6310000081565b6100a76100f9366004610790565b6104ba565b6100a761010c366004610790565b6105e6565b6100a760ff81565b6100a7662386f26fc1000081565b6100a7670893b2a3668e24c081565b5f806101b6670de0b6b3a7640000866001600160a01b031663c146bf946040518163ffffffff1660e01b8152600401602060405180830381865afa158015610180573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906101a491906107c2565b6101ae90866107ed565b8691906106ba565b90505f6101cc8285670de0b6b3a76400006106ba565b60405163c661065760e01b81525f600482018190529192506001600160a01b0388169063c661065790602401602060405180830381865afa158015610213573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906102379190610806565b6001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015610272573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906102969190610821565b6102a190600a610921565b90506103318187896001600160a01b031663ed8e84f360405180604001604052808881526020018981525060016040518363ffffffff1660e01b81526004016102eb929190610957565b602060405180830381865afa158015610306573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061032a91906107c2565b91906106ba565b93505050505b9392505050565b5f80610388670de0b6b3a7640000866001600160a01b031663c146bf946040518163ffffffff1660e01b8152600401602060405180830381865afa158015610180573d5f803e3d5ffd5b90505f61039e8285670de0b6b3a76400006106ba565b60405163c661065760e01b81525f600482018190529192506001600160a01b0388169063c661065790602401602060405180830381865afa1580156103e5573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906104099190610806565b6001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015610444573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906104689190610821565b61047390600a610921565b90506103318187896001600160a01b0316638d8ea7276040518060400160405280888152602001898152506040518263ffffffff1660e01b81526004016102eb9190610974565b5f662386f26fc1000068056bc75e2d6310000082808080805b886104de8888610982565b11156105c15760ff83111561052d5786868a6104fa8383610982565b604051631b024b2b60e21b8152600481019490945260248401929092526044830152606482015260840160405180910390fd5b61054c670893b2a3668e24c0670de0b6b3a764000061032a8a8a610982565b6105569087610982565b9450610577670893b2a3668e24c0670de0b6b3a764000061032a8a8a610982565b61058190886107ed565b935061058e8b8b8761033e565b915061059b8b8b8661033e565b9050808211156105ad578395506105b1565b8496505b6105ba83610995565b92506104d3565b60026105cd87896107ed565b6105d791906109c1565b9b9a5050505050505050505050565b5f662386f26fc1000068056bc75e2d6310000082808080805b8861060a8888610982565b11156105c15760ff8311156106265786868a6104fa8383610982565b610645670893b2a3668e24c0670de0b6b3a764000061032a8a8a610982565b61064f9087610982565b9450610670670893b2a3668e24c0670de0b6b3a764000061032a8a8a610982565b61067a90886107ed565b93506106878b8b87610136565b91506106948b8b86610136565b9050808211156106a6578395506106aa565b8496505b6106b383610995565b92506105ff565b5f838302815f1985870982811083820303915050805f036106ee578382816106e4576106e46109ad565b0492505050610337565b80841161070e5760405163227bc15360e01b815260040160405180910390fd5b5f848688095f868103871696879004966002600389028118808a02820302808a02820302808a02820302808a02820302808a02820302808a02909103029181900381900460010186841190950394909402919094039290920491909117919091029150509392505050565b6001600160a01b038116811461078d575f80fd5b50565b5f805f606084860312156107a2575f80fd5b83356107ad81610779565b95602085013595506040909401359392505050565b5f602082840312156107d2575f80fd5b5051919050565b634e487b7160e01b5f52601160045260245ffd5b80820180821115610800576108006107d9565b92915050565b5f60208284031215610816575f80fd5b815161033781610779565b5f60208284031215610831575f80fd5b815160ff81168114610337575f80fd5b600181815b8085111561087b57815f1904821115610861576108616107d9565b8085161561086e57918102915b93841c9390800290610846565b509250929050565b5f8261089157506001610800565b8161089d57505f610800565b81600181146108b357600281146108bd576108d9565b6001915050610800565b60ff8411156108ce576108ce6107d9565b50506001821b610800565b5060208310610133831016604e8410600b84101617156108fc575081810a610800565b6109068383610841565b805f1904821115610919576109196107d9565b029392505050565b5f61033760ff841683610883565b805f5b6002811015610951578151845260209384019390910190600101610932565b50505050565b60608101610965828561092f565b82151560408301529392505050565b60408101610800828461092f565b81810381811115610800576108006107d9565b5f600182016109a6576109a66107d9565b5060010190565b634e487b7160e01b5f52601260045260245ffd5b5f826109db57634e487b7160e01b5f52601260045260245ffd5b50049056fea2646970667358221220bef52e5e9362f14f1386fabc481752dbb11e478700c2e8ca03c835ae1729dea564736f6c63430008140033
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
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.