Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Uniswap V3 Price Feed #101

Merged
merged 35 commits into from
Oct 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
ecb9fd1
feat(protocol): add partial implementation of UniswapV3PriceFeed
scorpion9979 Mar 11, 2023
65ee8ad
fix(protocol): update TickMath and FullMath to solidity v0.8.4
scorpion9979 Mar 13, 2023
e01b183
feat(protocol): set UniswapV3Pool feed description dynamically in con…
scorpion9979 Mar 13, 2023
f550472
chore(protocol): add deployment script for UniswapV3PriceFeed
scorpion9979 Mar 13, 2023
2ab3a07
chore(protocol): add missing uniswapV3PriceFeed import in "packages/p…
scorpion9979 Mar 13, 2023
70cec57
fix(protocol): loss of precision issue in "getPriceInternal" function
scorpion9979 Mar 14, 2023
39c9ce1
feat(protocol): handle math edge cases in "getPriceInternal"
scorpion9979 Mar 15, 2023
24993e3
perf(protocol): refactor "token0Decimals" and "token1Decimals" into i…
scorpion9979 Mar 15, 2023
895f652
perf(protocol): remove superfluous "mTwapInterval" memory var from "g…
scorpion9979 Mar 15, 2023
fb5eeb4
test(protocol): setup unit tests for UniswapV3PriceFeed
scorpion9979 Mar 16, 2023
9397f43
test(protocol): test "getRoundData" function in UniswapV3PriceFeed
scorpion9979 Mar 17, 2023
6ba8822
test(protocol): test "decimals" function in UniswapV3PriceFeed
scorpion9979 Mar 17, 2023
0157f02
fix(protocol): handle missed edge case in "getPriceInternal" function
scorpion9979 Mar 17, 2023
9a99929
test(protocol): add GodModeUniswapV3PriceFeed test contract to modify…
scorpion9979 Mar 21, 2023
35aa406
test(protocol): add "calculateTick" and "tickToTokenPrices" to mirror…
scorpion9979 Mar 21, 2023
e5ac32e
refactor(protocol): re-implement "description" as a view function
scorpion9979 Mar 22, 2023
baf1a9f
chore(protocol): add custom compiler settings for UniswapV3PriceFeed.sol
scorpion9979 Mar 22, 2023
ac2aa67
chore(protocol): set "settings.optimizer.details.yul" to "true" in ha…
scorpion9979 Mar 22, 2023
9a1f6f2
Merge branch 'main' into feat/uniswap-v3-price-feed
scorpion9979 Mar 31, 2023
98162ed
chore(protocol): update solidity version to 0.8.4 in IUniswapV3Pool.s…
scorpion9979 Mar 31, 2023
9afe525
feat(protocol): validate TWAP criteria in UniswapV3PriceFeed constructor
scorpion9979 Apr 21, 2023
952add3
docs(protocol): reword comment in UniswapV3PriceFeed.sol
scorpion9979 Apr 21, 2023
1cc8b55
test(protocol): fix "unitFixtureUniswapV3PriceFeed" test fixture
scorpion9979 Apr 26, 2023
68a7c04
test(protocol): constructor in UniswapV3PriceFeed
scorpion9979 Apr 26, 2023
c68636a
docs(protocol): improve NatSpec comments in UniswapV3PriceFeed
scorpion9979 Apr 27, 2023
da7421e
feat(protocol): add "MINIMUM_CARDINALITY" constant in UniswapV3PriceFeed
scorpion9979 Apr 27, 2023
e391250
feat(protocol): add "baseAsset" state var to UniswapV3PriceFeed
scorpion9979 Apr 27, 2023
80d6e1c
refactor(protocol): rename "refAsset" to "quoteAsset" in UniswapV3Pri…
scorpion9979 Apr 28, 2023
8bf1767
fix(protocol): bug in TWAP interval calculation in UniswapV3PriceFeed…
scorpion9979 Apr 28, 2023
3148fad
feat(protocol): remove redundant IUniswapV3PriceFeed__ZeroAddressPool…
scorpion9979 Apr 28, 2023
d3f88f9
docs(protocol): improve comments in UniswapV3PriceFeed
scorpion9979 Apr 28, 2023
f0a7e0d
feat(protocol): calculate minimum cardinality instead of using const …
scorpion9979 Oct 11, 2023
785636a
feat(protocol): add a max price bound in UniswapV3PriceFeed
scorpion9979 Oct 12, 2023
8d65cae
chore(protocol): fix bug in UniswapV3PriceFeed deployment script
scorpion9979 Oct 12, 2023
6a01de9
feat(protocol): use "cardinalityNext" for TWAP criteria logic instead…
scorpion9979 Oct 12, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/constants/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export * from "./addresses";
export * from "./chains";
export * from "./gas";
export * from "./oracles";
export * from "./prices";
export * from "./protocol";
export * from "./tokens";
9 changes: 9 additions & 0 deletions packages/constants/src/oracles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { BigNumber } from "@ethersproject/bignumber";

export const DEFAULT_CARDINALITY: number = 144;
export const DEFAULT_TWAP_INTERVAL: number = 1800;
export const Q192: BigNumber = BigNumber.from("6277101735386680763835789423207666416102355444464034512896");
export const TICKS = {
lowerBound: -798544800,
upperBound: 798544800,
};
3 changes: 3 additions & 0 deletions packages/errors/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ export { OwnableErrors } from "./external";
// flashSwap.ts
export { FlashUniswapV2Errors, FlashUniswapV3Errors } from "./flashSwap";

// oracles.ts
export { UniswapV3PriceFeedErrors } from "./oracles";

// protocol.ts
export {
BalanceSheetErrors,
Expand Down
5 changes: 5 additions & 0 deletions packages/errors/src/oracles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export enum UniswapV3PriceFeedErrors {
QUOTE_ASSET_NOT_IN_POOL = "IUniswapV3PriceFeed__QuoteAssetNotInPool",
TWAP_CRITERIA_NOT_SATISFIED = "IUniswapV3PriceFeed__TwapCriteriaNotSatisfied",
MAX_PRICE_LESS_THAN_OR_EQUAL_TO_ZERO = "IUniswapV3PriceFeed__MaxPriceLessThanOrEqualToZero",
}
3 changes: 1 addition & 2 deletions packages/flash-swap/contracts/uniswap-v3/NoDelegateCall.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@
// solhint-disable
pragma solidity =0.7.6;

/// @title Prevents delegatecall to a contract
/// @notice Base contract that provides a modifier for preventing delegatecall to methods in a child contract
/// @dev https://raw.githubusercontent.com/Uniswap/v3-core/v1.0.0/contracts/NoDelegateCall.sol
abstract contract NoDelegateCall {
/// @dev The original address of this contract
address private immutable original;
Expand Down
1 change: 1 addition & 0 deletions packages/flash-swap/contracts/uniswap-v3/UniswapV3Pool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import "@uniswap/v3-core/contracts/interfaces/callback/IUniswapV3MintCallback.so
import "@uniswap/v3-core/contracts/interfaces/callback/IUniswapV3SwapCallback.sol";
import "@uniswap/v3-core/contracts/interfaces/callback/IUniswapV3FlashCallback.sol";

/// @dev https://raw.githubusercontent.com/Uniswap/v3-core/v1.0.0/contracts/UniswapV3Pool.sol
contract UniswapV3Pool is IUniswapV3Pool, NoDelegateCall {
using LowGasSafeMath for uint256;
using LowGasSafeMath for int256;
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ pragma solidity >=0.8.4;
/// @title IAggregatorV3
/// @author Hifi
/// @dev Forked from Chainlink
/// github.com/smartcontractkit/chainlink/blob/v1.2.0/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol
/// https://github.com/smartcontractkit/chainlink/blob/v1.2.0/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol
interface IAggregatorV3 {
function decimals() external view returns (uint8);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.4;

/// @title IUniswapV3Pool
/// @author Hifi
/// @dev Forked from Uniswap
/// https://github.com/Uniswap/v3-core/blob/v1.0.0/contracts/interfaces/IUniswapV3Factory.sol
interface IUniswapV3Pool {
function factory() external view returns (address);

function fee() external view returns (uint24);

function initialize(uint160 sqrtPriceX96) external;

function maxLiquidityPerTick() external view returns (uint128);

function observe(uint32[] calldata secondsAgos)
external
view
returns (int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s);

function observations(uint256 index)
external
view
returns (
uint32 blockTimestamp,
int56 tickCumulative,
uint160 secondsPerLiquidityCumulativeX128,
bool initialized
);

function slot0()
external
view
returns (
uint160 sqrtPriceX96,
int24 tick,
uint16 observationIndex,
uint16 observationCardinality,
uint16 observationCardinalityNext,
uint8 feeProtocol,
bool unlocked
);

function tickSpacing() external view returns (int24);

function token0() external view returns (address);

function token1() external view returns (address);
}
131 changes: 131 additions & 0 deletions packages/protocol/contracts/external/uniswap/libraries/FullMath.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
// SPDX-License-Identifier: MIT
// solhint-disable max-line-length, no-inline-assembly, reason-string
pragma solidity ^0.8.4;

/// @title FullMath
/// @author Hifi
/// @dev Forked from Euler
/// https://github.com/euler-xyz/euler-contracts/blob/dfaa7788b17ac7c2a826a3ed242d7181998a778f/contracts/vendor/FullMath.sol
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) {
unchecked {
// 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.
uint256 twos = denominator & (~denominator + 1);

// 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;
}
}

/// @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) {
unchecked {
result = mulDiv(a, b, denominator);
if (mulmod(a, b, denominator) > 0) {
require(result < type(uint256).max);
result++;
}
}
}
}
Loading