Skip to content
This repository has been archived by the owner on Oct 26, 2022. It is now read-only.

SingleInteractionTWAP #24

Open
wants to merge 6 commits into
base: master
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,032 changes: 1,032 additions & 0 deletions contracts/flat/StorageProof0.sol

Large diffs are not rendered by default.

1,031 changes: 1,031 additions & 0 deletions contracts/flat/StorageProof1.sol

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion contracts/interfaces/IOracle.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;
pragma solidity ^0.6.8;

interface IOracle {
/// @notice Get the latest exchange rate.
Expand Down
2 changes: 1 addition & 1 deletion contracts/libraries/FixedPoint.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.6.12;
pragma solidity >=0.4.0;
import "./FullMath.sol";

// solhint-disable
Expand Down
2 changes: 1 addition & 1 deletion contracts/libraries/FullMath.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: CC-BY-4.0
pragma solidity 0.6.12;
pragma solidity >=0.4.0;

// solhint-disable

Expand Down
59 changes: 47 additions & 12 deletions contracts/oracles/SimpleSLPTWAP0Oracle.sol
Original file line number Diff line number Diff line change
@@ -1,21 +1,31 @@
// SPDX-License-Identifier: AGPL-3.0-only
// Using the same Copyleft License as in the original Repository
pragma solidity 0.6.12;
pragma solidity 0.6.8;
pragma experimental ABIEncoderV2;
import "../interfaces/IOracle.sol";
import "@boringcrypto/boring-solidity/contracts/libraries/BoringMath.sol";
import "@sushiswap/core/contracts/uniswapv2/interfaces/IUniswapV2Factory.sol";
import "@sushiswap/core/contracts/uniswapv2/interfaces/IUniswapV2Pair.sol";
import '@keydonix/uniswap-oracle-contracts/source/IUniswapV2Pair.sol';
import { UniswapOracle } from '@keydonix/uniswap-oracle-contracts/source/UniswapOracle.sol';
import "../libraries/FixedPoint.sol";

// solhint-disable not-rely-on-time


library BoringMath {
function mul(uint256 a, uint256 b) internal pure returns (uint256 c) {
require(b == 0 || (c = a * b) / b == a, "BoringMath: Mul Overflow");
}

}

// adapted from https://github.com/Uniswap/uniswap-v2-periphery/blob/master/contracts/examples/ExampleSlidingWindowOracle.sol

contract SimpleSLPTWAP0Oracle is IOracle {
contract SimpleSLPTWAP0Oracle is IOracle, UniswapOracle {
using FixedPoint for *;
using BoringMath for uint256;
uint256 public constant PERIOD = 5 minutes;
uint8 public constant MIN_BLOCKS = 25;
uint8 public constant MAX_BLOCKS = 125;

struct PairInfo {
uint256 priceCumulativeLast;
Expand All @@ -38,12 +48,41 @@ contract SimpleSLPTWAP0Oracle is IOracle {
return priceCumulative;
}

function updateWithProof(bytes memory data, ProofData memory proofData) public returns (bool, uint256) {
IUniswapV2Pair pair = abi.decode(data, (IUniswapV2Pair));
uint256 historicBlockTimestamp;
uint256 historicPriceCumulativeLast;
{
// Stack-too-deep workaround, manual scope
// Side-note: wtf Solidity?
uint112 reserve0;
uint112 reserve1;
uint256 reserveTimestamp;
(historicBlockTimestamp, , historicPriceCumulativeLast, reserve0, reserve1, reserveTimestamp) = verifyBlockAndExtractReserveData(pair, MIN_BLOCKS, MAX_BLOCKS, token1Slot, proofData);
uint256 secondsBetweenReserveUpdateAndHistoricBlock = historicBlockTimestamp - reserveTimestamp;
// bring old record up-to-date, in case there was no cumulative update in provided historic block itself
if (secondsBetweenReserveUpdateAndHistoricBlock > 0) {
historicPriceCumulativeLast += secondsBetweenReserveUpdateAndHistoricBlock * uint256(FixedPoint.fraction(reserve1, reserve0)._x);
}
}
uint32 blockTimestamp = uint32(block.timestamp);
uint32 timeElapsed = blockTimestamp - uint32(historicBlockTimestamp); // overflow is desired
uint256 priceCumulative = _get(pair, blockTimestamp);
pairs[pair].priceAverage = FixedPoint
.uq112x112(uint224((priceCumulative - historicPriceCumulativeLast) / timeElapsed))
.mul(10**18)
.decode144();
pairs[pair].blockTimestampLast = blockTimestamp;
pairs[pair].priceCumulativeLast = priceCumulative;

return (true, pairs[pair].priceAverage);
}

function getDataParameter(IUniswapV2Pair pair) public pure returns (bytes memory) {
return abi.encode(pair);
}

// Get the latest exchange rate, if no valid (recent) rate is available, return false
/// @inheritdoc IOracle
function get(bytes calldata data) external override returns (bool, uint256) {
IUniswapV2Pair pair = abi.decode(data, (IUniswapV2Pair));
uint32 blockTimestamp = uint32(block.timestamp);
Expand All @@ -69,8 +108,7 @@ contract SimpleSLPTWAP0Oracle is IOracle {
}

// Check the last exchange rate without any state changes
/// @inheritdoc IOracle
function peek(bytes calldata data) public view override returns (bool, uint256) {
function peek(bytes memory data) public view override returns (bool, uint256) {
IUniswapV2Pair pair = abi.decode(data, (IUniswapV2Pair));
uint32 blockTimestamp = uint32(block.timestamp);
if (pairs[pair].blockTimestampLast == 0) {
Expand All @@ -89,20 +127,17 @@ contract SimpleSLPTWAP0Oracle is IOracle {
}

// Check the current spot exchange rate without any state changes
/// @inheritdoc IOracle
function peekSpot(bytes calldata data) external view override returns (uint256 rate) {
IUniswapV2Pair pair = abi.decode(data, (IUniswapV2Pair));
(uint256 reserve0, uint256 reserve1, ) = pair.getReserves();
rate = reserve1.mul(1e18) / reserve0;
}

/// @inheritdoc IOracle
function name(bytes calldata) public view override returns (string memory) {
function name(bytes memory) public view override returns (string memory) {
return "SushiSwap TWAP";
}

/// @inheritdoc IOracle
function symbol(bytes calldata) public view override returns (string memory) {
function symbol(bytes memory) public view override returns (string memory) {
return "S";
}
}
58 changes: 46 additions & 12 deletions contracts/oracles/SimpleSLPTWAP1Oracle.sol
Original file line number Diff line number Diff line change
@@ -1,21 +1,30 @@
// SPDX-License-Identifier: AGPL-3.0-only
// Using the same Copyleft License as in the original Repository
pragma solidity 0.6.12;
pragma solidity 0.6.8;
pragma experimental ABIEncoderV2;
import "../interfaces/IOracle.sol";
import "@boringcrypto/boring-solidity/contracts/libraries/BoringMath.sol";
import "@sushiswap/core/contracts/uniswapv2/interfaces/IUniswapV2Factory.sol";
import "@sushiswap/core/contracts/uniswapv2/interfaces/IUniswapV2Pair.sol";
import '@keydonix/uniswap-oracle-contracts/source/IUniswapV2Pair.sol';
import { UniswapOracle } from '@keydonix/uniswap-oracle-contracts/source/UniswapOracle.sol';
import "../libraries/FixedPoint.sol";

// solhint-disable not-rely-on-time

library BoringMath {
function mul(uint256 a, uint256 b) internal pure returns (uint256 c) {
require(b == 0 || (c = a * b) / b == a, "BoringMath: Mul Overflow");
}

}

// adapted from https://github.com/Uniswap/uniswap-v2-periphery/blob/master/contracts/examples/ExampleSlidingWindowOracle.sol

contract SimpleSLPTWAP1Oracle is IOracle {
contract SimpleSLPTWAP1Oracle is IOracle, UniswapOracle {
using FixedPoint for *;
using BoringMath for uint256;
uint256 public constant PERIOD = 5 minutes;
uint8 public constant MIN_BLOCKS = 25;
uint8 public constant MAX_BLOCKS = 125;

struct PairInfo {
uint256 priceCumulativeLast;
Expand All @@ -38,12 +47,41 @@ contract SimpleSLPTWAP1Oracle is IOracle {
return priceCumulative;
}

function updateWithProof(bytes memory data, ProofData memory proofData) public returns (bool, uint256) {
IUniswapV2Pair pair = abi.decode(data, (IUniswapV2Pair));
uint256 historicBlockTimestamp;
uint256 historicPriceCumulativeLast;
{
// Stack-too-deep workaround, manual scope
// Side-note: wtf Solidity?
uint112 reserve0;
uint112 reserve1;
uint256 reserveTimestamp;
(historicBlockTimestamp, , historicPriceCumulativeLast, reserve0, reserve1, reserveTimestamp) = verifyBlockAndExtractReserveData(pair, MIN_BLOCKS, MAX_BLOCKS, token1Slot, proofData);
uint256 secondsBetweenReserveUpdateAndHistoricBlock = historicBlockTimestamp - reserveTimestamp;
// bring old record up-to-date, in case there was no cumulative update in provided historic block itself
if (secondsBetweenReserveUpdateAndHistoricBlock > 0) {
historicPriceCumulativeLast += secondsBetweenReserveUpdateAndHistoricBlock * uint256(FixedPoint.fraction(reserve0, reserve1)._x);
}
}
uint32 blockTimestamp = uint32(block.timestamp);
uint32 timeElapsed = blockTimestamp - uint32(historicBlockTimestamp); // overflow is desired
uint256 priceCumulative = _get(pair, blockTimestamp);
pairs[pair].priceAverage = FixedPoint
.uq112x112(uint224((priceCumulative - historicPriceCumulativeLast) / timeElapsed))
.mul(10**18)
.decode144();
pairs[pair].blockTimestampLast = blockTimestamp;
pairs[pair].priceCumulativeLast = priceCumulative;

return (true, pairs[pair].priceAverage);
}

function getDataParameter(IUniswapV2Pair pair) public pure returns (bytes memory) {
return abi.encode(pair);
}

// Get the latest exchange rate, if no valid (recent) rate is available, return false
/// @inheritdoc IOracle
function get(bytes calldata data) external override returns (bool, uint256) {
IUniswapV2Pair pair = abi.decode(data, (IUniswapV2Pair));
uint32 blockTimestamp = uint32(block.timestamp);
Expand All @@ -69,8 +107,7 @@ contract SimpleSLPTWAP1Oracle is IOracle {
}

// Check the last exchange rate without any state changes
/// @inheritdoc IOracle
function peek(bytes calldata data) public view override returns (bool, uint256) {
function peek(bytes memory data) public view override returns (bool, uint256) {
IUniswapV2Pair pair = abi.decode(data, (IUniswapV2Pair));
uint32 blockTimestamp = uint32(block.timestamp);
if (pairs[pair].blockTimestampLast == 0) {
Expand All @@ -89,20 +126,17 @@ contract SimpleSLPTWAP1Oracle is IOracle {
}

// Check the current spot exchange rate without any state changes
/// @inheritdoc IOracle
function peekSpot(bytes calldata data) external view override returns (uint256 rate) {
IUniswapV2Pair pair = abi.decode(data, (IUniswapV2Pair));
(uint256 reserve0, uint256 reserve1, ) = pair.getReserves();
rate = reserve0.mul(1e18) / reserve1;
}

/// @inheritdoc IOracle
function name(bytes calldata) public view override returns (string memory) {
function name(bytes memory) public view override returns (string memory) {
return "SushiSwap TWAP";
}

/// @inheritdoc IOracle
function symbol(bytes calldata) public view override returns (string memory) {
function symbol(bytes memory) public view override returns (string memory) {
return "S";
}
}
100 changes: 100 additions & 0 deletions hardhat.config.js
Original file line number Diff line number Diff line change
@@ -1 +1,101 @@
module.exports = require("@sushiswap/hardhat-framework").config.hardhat(require("./settings").hardhat)
const fs = require("fs")

function getSortedFiles(dependenciesGraph) {
const tsort = require("tsort")
const graph = tsort()

const filesMap = {}
const resolvedFiles = dependenciesGraph.getResolvedFiles()
resolvedFiles.forEach((f) => (filesMap[f.sourceName] = f))

for (const [from, deps] of dependenciesGraph.entries()) {
for (const to of deps) {
graph.add(to.sourceName, from.sourceName)
}
}

const topologicalSortedNames = graph.sort()

// If an entry has no dependency it won't be included in the graph, so we
// add them and then dedup the array
const withEntries = topologicalSortedNames.concat(resolvedFiles.map((f) => f.sourceName))

const sortedNames = [...new Set(withEntries)]
return sortedNames.map((n) => filesMap[n])
}

function getFileWithoutImports(resolvedFile) {
const IMPORT_SOLIDITY_REGEX = /^\s*import(\s+)[\s\S]*?;\s*$/gm

return resolvedFile.content.rawContent.replace(IMPORT_SOLIDITY_REGEX, "").trim()
}

subtask("flat:get-flattened-sources", "Returns all contracts and their dependencies flattened")
.addOptionalParam("files", undefined, undefined, types.any)
.addOptionalParam("output", undefined, undefined, types.string)
.setAction(async ({ files, output }, { run }) => {
const dependencyGraph = await run("flat:get-dependency-graph", { files })
console.log(dependencyGraph)

let flattened = ""

if (dependencyGraph.getResolvedFiles().length === 0) {
return flattened
}

const sortedFiles = getSortedFiles(dependencyGraph)

let isFirst = true
for (const file of sortedFiles) {
if (!isFirst) {
flattened += "\n"
}
flattened += `// File ${file.getVersionedName()}\n`
flattened += `${getFileWithoutImports(file)}\n`

isFirst = false
}

// Remove every line started with "// SPDX-License-Identifier:"
flattened = flattened.replace(/SPDX-License-Identifier:/gm, "License-Identifier:")

flattened = `// SPDX-License-Identifier: MIXED\n\n${flattened}`

// Remove every line started with "pragma experimental ABIEncoderV2;" except the first one
flattened = flattened.replace(/pragma experimental ABIEncoderV2;\n/gm, ((i) => (m) => (!i++ ? m : ""))(0))

flattened = flattened.trim()
if (output) {
console.log("Writing to", output)
fs.writeFileSync(output, flattened)
return ""
}
return flattened
})

subtask("flat:get-dependency-graph")
.addOptionalParam("files", undefined, undefined, types.any)
.setAction(async ({ files }, { run }) => {
const sourcePaths = files === undefined ? await run("compile:solidity:get-source-paths") : files.map((f) => fs.realpathSync(f))

const sourceNames = await run("compile:solidity:get-source-names", {
sourcePaths,
})

const dependencyGraph = await run("compile:solidity:get-dependency-graph", { sourceNames })

return dependencyGraph
})

task("flat", "Flattens and prints contracts and their dependencies")
.addOptionalVariadicPositionalParam("files", "The files to flatten", undefined, types.inputFile)
.addOptionalParam("output", "Specify the output file", undefined, types.string)
.setAction(async ({ files, output }, { run }) => {
console.log(
await run("flat:get-flattened-sources", {
files,
output,
})
)
})
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
},
"dependencies": {
"@boringcrypto/boring-solidity": "boringcrypto/BoringSolidity#371a01c11465eb1c881193c60a53c0bf15ee8a19",
"@keydonix/uniswap-oracle-contracts": "^1.0.6",
"@sushiswap/bentobox-sdk": "sushiswap/bentobox-sdk#e3f809870b86afbd57e0930662221d2362a160db",
"@sushiswap/core": "^1.4.2",
"hardhat-deploy-ethers": "^0.3.0-beta.7"
Expand Down
38 changes: 38 additions & 0 deletions settings.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,45 @@
module.exports = {
hardhat: {
solidity: {
compilers: [
{
version: "0.6.12",
settings: {
optimizer: {
enabled: true,
runs: 200,
},
},
},
{
version: "0.6.8",
settings: {
optimizer: {
enabled: true,
runs: 200,
},
},
}
],
overrides: {
"contracts/oracles/SimpleSLPTWAP1Oracle.sol": {
version: "0.6.8",
settings: {
optimizer: {
enabled: true,
runs: 400,
},
},
},
"contracts/oracles/SimpleSLPTWAP0Oracle.sol": {
version: "0.6.8",
settings: {
optimizer: {
enabled: true,
runs: 400,
},
},
},
"contracts/KashiPair.sol": {
version: "0.6.12",
settings: {
Expand Down
Loading