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

Camelot v2 style oracle #1

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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 .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
/out
.env
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,6 @@
[submodule "lib/ds-value"]
path = lib/ds-value
url = https://github.com/reflexer-labs/ds-value.git
[submodule "lib/forge-std"]
path = lib/forge-std
url = https://github.com/foundry-rs/forge-std
426 changes: 426 additions & 0 deletions CamelotPair.sol

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions lib/forge-std
Submodule forge-std added at 5f1b1c
77 changes: 77 additions & 0 deletions script/Deploy.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
pragma experimental ABIEncoderV2;

import {UniswapConverterBasicAveragePriceFeedMedianizer} from
"../src/UniswapConverterBasicAveragePriceFeedMedianizer.sol";
import {Script} from "forge-std/Script.sol";
import {MockRewardRelayer} from "../src/test/relayer/MockRewardRelayer.sol";
import {Test} from "forge-std/Test.sol";
import "ds-test/test.sol";

// BROADCAST
// source .env && forge script DeployUniswapConverterBasicAveragePriceFeedMedianizer --with-gas-price 2000000000 -vvvvv --rpc-url $ARB_MAINNET_RPC --broadcast --verify --etherscan-api-key $ARB_ETHERSCAN_API_KEY --sender $DEFAULT_KEY_PUBLIC_ADDRESS --account defaultKey

// SIMULATE
// source .env && forge script DeployUniswapConverterBasicAveragePriceFeedMedianizer --with-gas-price 2000000000 -vvvvv --rpc-url $ARB_MAINNET_RPC --sender $DEFAULT_KEY_PUBLIC_ADDRESS

contract DeployUniswapConverterBasicAveragePriceFeedMedianizer is Script, DSTest {
UniswapConverterBasicAveragePriceFeedMedianizer public uniswapEPendleUsdMedianizer;
MockRewardRelayer noRewardRelayer;

address constant MAINNET_PENDLE_USD_DENOMINATED_ORACLE = 0x07f2b47d5Ca2ee488c1Fb013f8d63181e22B9dAa;
address constant MAINNET_CAMELOT_V2_FACTORY = 0x6EcCab422D763aC031210895C81787E87B43A652;
address constant MAINNET_E_PENDLE = 0x3EaBE18eAE267D1B57f917aBa085bb5906114600;
address constant MAINNET_PENDLE = 0x0c880f6761F1af8d9Aa9C466984b80DAb9a8c9e8;

uint256 uniswapMedianizerDefaultAmountIn = 1 ether;
uint256 uniswapMedianizerWindowSize = 86400; // 24 hours
uint256 converterScalingFactor = 1 ether;
uint8 uniswapMedianizerGranularity = 24; // 1 hour (ie. 86400 / 24 = 3600)

function run() public {
vm.startBroadcast();

uniswapEPendleUsdMedianizer = new UniswapConverterBasicAveragePriceFeedMedianizer(
MAINNET_PENDLE_USD_DENOMINATED_ORACLE,
MAINNET_CAMELOT_V2_FACTORY,
uniswapMedianizerDefaultAmountIn,
uniswapMedianizerWindowSize,
converterScalingFactor,
uniswapMedianizerGranularity
);

noRewardRelayer = new MockRewardRelayer();

uniswapEPendleUsdMedianizer.modifyParameters("relayer", address(noRewardRelayer));
uniswapEPendleUsdMedianizer.modifyParameters("targetToken", MAINNET_E_PENDLE);
uniswapEPendleUsdMedianizer.modifyParameters("denominationToken", MAINNET_PENDLE);

// vm.warp(now + 3599);

uniswapEPendleUsdMedianizer.updateResult(address(this));
// (uint256 uniTimestamp, uint256 price0Cumulative, uint256 price1Cumulative) =
// uniswapEPendleUsdMedianizer.uniswapObservations(0);
// (uint256 medianPrice, bool isValid) = uniswapEPendleUsdMedianizer.getResultWithValidity();

// assertEq(uint256(uniswapEPendleUsdMedianizer.observationIndexOf(now)), 0);
// assertEq(medianPrice, 0);
// assertTrue(!isValid);
// assertEq(uniTimestamp, now);

// vm.warp(now + uniswapEPendleUsdMedianizer.periodSize());
// // vm.warp(block.timestamp + 30 minutes);
// uniswapEPendleUsdMedianizer.updateResult(address(0));
// (uint256 result,) = uniswapEPendleUsdMedianizer.getResultWithValidity();
// emit log_uint(result);

// emit log_uint(block.timestamp);
// // vm.warp(block.timestamp + 30 minutes);
// // vm.roll(100);
// vm.warp(now + 3599);

// emit log_uint(block.timestamp);

// uniswapEPendleUsdMedianizer.updateResult(address(0));

// uniswapEPendleUsdMedianizer.getResultWithValidity();
}
}
67 changes: 67 additions & 0 deletions script/Skim.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
pragma solidity 0.6.7;
pragma experimental ABIEncoderV2;

import {Script} from "forge-std/Script.sol";
import {Test} from "forge-std/Test.sol";

import {UniswapConverterBasicAveragePriceFeedMedianizer} from
"../src/UniswapConverterBasicAveragePriceFeedMedianizer.sol";

import {MockRewardRelayer} from "../src/test/relayer/MockRewardRelayer.sol";

import {IUniswapV2Pair} from "../src/univ2/interfaces/IUniswapV2Pair.sol";
import {IUniswapV2Factory} from "../src/univ2/interfaces/IUniswapV2Factory.sol";
import {IERC20} from "../src/univ2/interfaces/IERC20.sol";
import "forge-std/console2.sol";

// BROADCAST
// source .env && forge script Skim --with-gas-price 2000000000 -vvvvv --rpc-url $ARB_MAINNET_RPC --broadcast --verify --etherscan-api-key $ARB_ETHERSCAN_API_KEY --sender $DEFAULT_KEY_PUBLIC_ADDRESS --account defaultKey

// SIMULATE
// source .env && forge script Skim --with-gas-price 2000000000 -vvvvv --rpc-url $ARB_MAINNET_RPC --sender $DEFAULT_KEY_PUBLIC_ADDRESS

contract Skim is Script, Test {
IUniswapV2Factory public camelotV2Factory;

IERC20 public token0;
IERC20 public token1;

address constant MAINNET_CAMELOT_V2_FACTORY = 0x6EcCab422D763aC031210895C81787E87B43A652;
address constant MAINNET_PENDLE = 0x0c880f6761F1af8d9Aa9C466984b80DAb9a8c9e8;
address constant MAINNET_E_PENDLE = 0x3EaBE18eAE267D1B57f917aBa085bb5906114600;

function run() public {
vm.startBroadcast();
camelotV2Factory = IUniswapV2Factory(MAINNET_CAMELOT_V2_FACTORY);

uint256 allPairsLength = camelotV2Factory.allPairsLength();

// for (uint256 i = 78; i < allPairsLength; i++) {
for (uint256 i = 151; i < 400; i++) {
address pairAddress = camelotV2Factory.allPairs(i);
IUniswapV2Pair pair = IUniswapV2Pair(pairAddress);

IERC20 token0 = IERC20(pair.token0());
IERC20 token1 = IERC20(pair.token1());

try pair.skim(msg.sender) {
uint256 balance0 = token0.balanceOf(msg.sender);
uint256 balance1 = token1.balanceOf(msg.sender);

if (balance0 != 0 || balance1 != 0) {
emit log_named_address("Pair Address:", pairAddress);

emit log_named_string("Token 0 Symbol:", token0.symbol());
emit log_named_decimal_uint("Token 0 Balance:", balance0, 18);

emit log_named_string("Token 1 Symbol:", token1.symbol());
emit log_named_decimal_uint("Token 1 Balance:", balance1, 18);
}
} catch {}
}

vm.stopBroadcast();
}
}

// 0xC0658411564271c780a7B9C82eEd6E5F7d7b1804::skim fails due to decimals 6
22 changes: 11 additions & 11 deletions src/UniswapConverterBasicAveragePriceFeedMedianizer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ contract UniswapConverterBasicAveragePriceFeedMedianizer is GebMath, UniswapV2Li
**/
function getFirstObservationsInWindow()
private view returns (UniswapObservation storage firstUniswapObservation, ConverterFeedObservation storage firstConverterFeedObservation) {
uint8 observationIndex = observationIndexOf(now);
uint8 observationIndex = observationIndexOf(block.timestamp);
// No overflow issue. If observationIndex + 1 overflows, result is still zero
uint8 firstObservationIndex = (observationIndex + 1) % granularity;
firstUniswapObservation = uniswapObservations[firstObservationIndex];
Expand All @@ -241,7 +241,7 @@ contract UniswapConverterBasicAveragePriceFeedMedianizer is GebMath, UniswapV2Li
UniswapObservation storage firstUniswapObservation,
) = getFirstObservationsInWindow();

uint timeElapsedSinceFirstUniObservation = subtract(now, firstUniswapObservation.timestamp);
uint timeElapsedSinceFirstUniObservation = subtract(block.timestamp, firstUniswapObservation.timestamp);
// We can only fetch a brand new median price if there's been enough price data gathered
if (both(timeElapsedSinceFirstUniObservation <= windowSize, timeElapsedSinceFirstUniObservation >= windowSize - periodSize * 2)) {
(address token0,) = sortTokens(targetToken, denominationToken);
Expand Down Expand Up @@ -321,14 +321,14 @@ contract UniswapConverterBasicAveragePriceFeedMedianizer is GebMath, UniswapV2Li
address finalFeeReceiver = (feeReceiver == address(0)) ? msg.sender : feeReceiver;

// Update the converter's median price first
try converterFeed.updateResult(finalFeeReceiver) {}
catch (bytes memory converterRevertReason) {
emit FailedConverterFeedUpdate(converterRevertReason);
}
// try converterFeed.updateResult(finalFeeReceiver) {}
// catch (bytes memory converterRevertReason) {
// emit FailedConverterFeedUpdate(converterRevertReason);
// }

// Get the observation for the current period
uint8 observationIndex = observationIndexOf(now);
uint256 timeElapsedSinceLatest = subtract(now, uniswapObservations[observationIndex].timestamp);
uint8 observationIndex = observationIndexOf(block.timestamp);
uint256 timeElapsedSinceLatest = subtract(block.timestamp, uniswapObservations[observationIndex].timestamp);
// We only want to commit updates once per period (i.e. windowSize / granularity)
require(timeElapsedSinceLatest > periodSize, "UniswapConverterBasicAveragePriceFeedMedianizer/not-enough-time-elapsed");

Expand All @@ -349,7 +349,7 @@ contract UniswapConverterBasicAveragePriceFeedMedianizer is GebMath, UniswapV2Li

// Calculate latest medianPrice
medianPrice = getMedianPrice(uniswapPrice0Cumulative, uniswapPrice1Cumulative);
lastUpdateTime = now;
lastUpdateTime = block.timestamp;
updates = addition(updates, 1);

emit UpdateResult(medianPrice, lastUpdateTime);
Expand All @@ -375,11 +375,11 @@ contract UniswapConverterBasicAveragePriceFeedMedianizer is GebMath, UniswapV2Li
require(hasValidValue, "UniswapConverterBasicAveragePriceFeedMedianizer/invalid-converter-price-feed");

// Add converter observation
latestConverterFeedObservation.timestamp = now;
latestConverterFeedObservation.timestamp = block.timestamp;
latestConverterFeedObservation.price = priceFeedValue;

// Add Uniswap observation
latestUniswapObservation.timestamp = now;
latestUniswapObservation.timestamp = block.timestamp;
latestUniswapObservation.price0Cumulative = uniswapPrice0Cumulative;
latestUniswapObservation.price1Cumulative = uniswapPrice1Cumulative;

Expand Down
2 changes: 1 addition & 1 deletion src/test/UniswapConsecutiveSlotsPriceFeedMedianizer.t.sol
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
pragma solidity 0.6.7;

import "ds-test/test.sol";
import "ds-weth/weth9.sol";
import "ds-weth/src/weth9.sol";
import "ds-token/token.sol";
import "geb-treasury-reimbursement/relayer/IncreasingRewardRelayer.sol";

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
pragma solidity 0.6.7;

import "ds-test/test.sol";
import "ds-weth/weth9.sol";
import "ds-weth/src/weth9.sol";
import "ds-token/token.sol";
import "geb-treasury-reimbursement/relayer/IncreasingRewardRelayer.sol";

Expand Down
2 changes: 1 addition & 1 deletion src/test/UniswapV3MedianizerMainnet.t.sol
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
pragma solidity 0.6.7;

import "ds-test/test.sol";
import "ds-weth/weth9.sol";
import "ds-weth/src/weth9.sol";
import "ds-token/token.sol";

import "../univ3/UniswapV3Pool.sol";
Expand Down
7 changes: 7 additions & 0 deletions src/test/relayer/MockRewardRelayer.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
pragma solidity 0.6.7;

contract MockRewardRelayer {
constructor() public {}

function reimburseCaller(address feeReceiver) external {}
}
36 changes: 20 additions & 16 deletions src/univ2/libs/UniswapV2OracleLibrary.sol
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
pragma solidity 0.6.7;

import '../interfaces/IUniswapV2Pair.sol';
import './FixedPointMath.sol';
import "../interfaces/IUniswapV2Pair.sol";
import "./FixedPointMath.sol";

// Contract with helper methods for oracles that are concerned with computing average prices
contract UniswapV2OracleLibrary is FixedPointMath {
Expand All @@ -11,23 +11,27 @@ contract UniswapV2OracleLibrary is FixedPointMath {
}

// Produces the cumulative price using counterfactuals to save gas and avoid a call to sync.
function currentCumulativePrices(
address pair
) internal view returns (uint price0Cumulative, uint price1Cumulative, uint32 blockTimestamp) {
function currentCumulativePrices(address pair)
internal
view
returns (uint256 price0Cumulative, uint256 price1Cumulative, uint32 blockTimestamp)
{
blockTimestamp = currentBlockTimestamp();
price0Cumulative = IUniswapV2Pair(pair).price0CumulativeLast();
price1Cumulative = IUniswapV2Pair(pair).price1CumulativeLast();
// price0Cumulative = IUniswapV2Pair(pair).price0CumulativeLast();
// price1Cumulative = IUniswapV2Pair(pair).price1CumulativeLast();

// if time has elapsed since the last update on the pair, mock the accumulated price values
(uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast) = IUniswapV2Pair(pair).getReserves();
if (blockTimestampLast != blockTimestamp) {
// subtraction overflow is desired
uint32 timeElapsed = blockTimestamp - blockTimestampLast;
// addition overflow is desired
// counterfactual
price0Cumulative += uint(frac(reserve1, reserve0)._x) * timeElapsed;
// counterfactual
price1Cumulative += uint(frac(reserve0, reserve1)._x) * timeElapsed;
}
// if (blockTimestampLast != blockTimestamp) {
// // subtraction overflow is desired
// uint32 timeElapsed = blockTimestamp - blockTimestampLast;
// // addition overflow is desired
// // counterfactual
// price0Cumulative += uint(frac(reserve1, reserve0)._x) * timeElapsed;
// // counterfactual
// price1Cumulative += uint(frac(reserve0, reserve1)._x) * timeElapsed;
// }
price0Cumulative = reserve0;
price1Cumulative = reserve1;
}
}