diff --git a/oracles/compund-uni-exploit/README.md b/oracles/compund-uni-exploit/README.md new file mode 100644 index 0000000..787272f --- /dev/null +++ b/oracles/compund-uni-exploit/README.md @@ -0,0 +1,47 @@ +## Compound Uni Trap + +## Running the Examples +``` +forge test +``` + +## Results +``` +Ran 1 test for test/CompoundUniTrapTest.t.sol:CompoundUniTrapTest +[PASS] testContinuousMonitoring() (gas: 154422) +Logs: + Setting for block number: 19290918 + Setting for block number: 19290919 + Setting for block number: 19290920 + Setting for block number: 19290921 + Setting for block number: 19290922 + Setting for block number: 19290923 + Setting for block number: 19290924 + Setting for block number: 19290925 + Setting for block number: 19290926 + Setting for block number: 19290927 + Block Number: 19290918 + Price from chainlink: 9474361440000000000 + Price from uniswap: 8340000000000000000 + Difference is: 1134361440000000000 + Deviation is: 11 + Block Number: 19290919 + Price from chainlink: 9713000000000000000 + Price from uniswap: 8340000000000000000 + Difference is: 1373000000000000000 + Deviation is: 14 + Block Number: 19290920 + Price from chainlink: 9713000000000000000 + Price from uniswap: 8340000000000000000 + Difference is: 1373000000000000000 + Deviation is: 14 + Block Number: 19290921 + Price from chainlink: 10158692020000000000 + Price from uniswap: 8340000000000000000 + Difference is: 1818692020000000000 + Deviation is: 17 + Price deviation detected at block: 19290921 + +Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 11.70s (15.68ms CPU time) +``` + diff --git a/oracles/compund-uni-exploit/foundry.toml b/oracles/compund-uni-exploit/foundry.toml new file mode 100644 index 0000000..25b918f --- /dev/null +++ b/oracles/compund-uni-exploit/foundry.toml @@ -0,0 +1,6 @@ +[profile.default] +src = "src" +out = "out" +libs = ["lib"] + +# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options diff --git a/oracles/compund-uni-exploit/src/CompoundUniTrap.sol b/oracles/compund-uni-exploit/src/CompoundUniTrap.sol new file mode 100644 index 0000000..3807e68 --- /dev/null +++ b/oracles/compund-uni-exploit/src/CompoundUniTrap.sol @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import {IERC20} from "forge-std/interfaces/IERC20.sol"; +import {Test, console} from "forge-std/Test.sol"; + +interface IUniswapAnchoredView { + function getUnderlyingPrice(address cToken) external view returns (uint256); +} + +interface AggregatorV3Interface { + function decimals() external view returns (uint8); + + function description() external view returns (string memory); + + function version() external view returns (uint256); + + function getRoundData( + uint80 _roundId + ) external view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound); + + function latestRoundData() + external + view + returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound); +} + +contract CompoundUniTrap { + struct PriceDataPoint { + uint256 chainlinkPrice; + uint256 uniswapPrice; + } + + AggregatorV3Interface public dataFeed = AggregatorV3Interface(0x553303d460EE0afB37EdFf9bE42922D8FF63220e); + IUniswapAnchoredView public uniswapView = IUniswapAnchoredView(0x50ce56A3239671Ab62f185704Caedf626352741e); + uint256 public deviationThreshold = 14; + + function getPricefromChainlink() public view returns (int) { + // prettier-ignore + ( + /* uint80 roundID */, + int answer, + /*uint startedAt*/, + /*uint timeStamp*/, + /*uint80 answeredInRound*/ + ) = dataFeed.latestRoundData(); + return answer; + } + + + function collect() public view returns (PriceDataPoint memory) { + int256 chainlinkPrice = getPricefromChainlink(); + uint256 adjustedChainlinkPrice = uint256(chainlinkPrice) * 10 ** 10; + + address tokenAddress = 0x35A18000230DA775CAc24873d00Ff85BccdeD550; + uint256 uniswapPrice = uniswapView.getUnderlyingPrice(tokenAddress); + + console.log("Price from chainlink: ", adjustedChainlinkPrice); + console.log("Price from uniswap: ", uniswapPrice); + + PriceDataPoint memory newPoint = PriceDataPoint({ + chainlinkPrice: adjustedChainlinkPrice, + uniswapPrice: uniswapPrice + }); + + return newPoint; + } + + + function isValid(PriceDataPoint[] calldata dataPoints) public view returns (bool) { + PriceDataPoint memory currentPrice = dataPoints[0]; + uint256 chainlinkPrice = currentPrice.chainlinkPrice; + uint256 uniswapPrice = currentPrice.uniswapPrice; + + // Calculate the price difference + uint256 priceDiff = (chainlinkPrice > uniswapPrice) ? chainlinkPrice - uniswapPrice : uniswapPrice - chainlinkPrice; + + console.log("Difference is: ",priceDiff); + + // Calculate the percentage difference + uint256 priceDeviation = (priceDiff * 100) / chainlinkPrice; + + console.log("Deviation is: ",priceDeviation); + // Check if the deviation exceeds the threshold + + if (priceDeviation > deviationThreshold) { + return false; + } + + return true; +} + +} \ No newline at end of file diff --git a/oracles/compund-uni-exploit/test/CompoundUniTrapTest.t.sol b/oracles/compund-uni-exploit/test/CompoundUniTrapTest.t.sol new file mode 100644 index 0000000..0fdd3e7 --- /dev/null +++ b/oracles/compund-uni-exploit/test/CompoundUniTrapTest.t.sol @@ -0,0 +1,42 @@ +pragma solidity ^0.8.13; + +import {Test, console} from "forge-std/Test.sol"; +import {CompoundUniTrap} from "../src/CompoundUniTrap.sol"; + +contract CompoundUniTrapTest is Test { + CompoundUniTrap public compoundUniTrap; + uint256 public currentBlockNumber = 19290918; + + //The attack takes place at block 19290921 + + //Defi Lab source + //https://github.com/SunWeb3Sec/DeFiHackLabs/blob/main/src/test/2024-02/CompoundUni_exp.sol + //https://etherscan.io/tx/0xaee0f8d1235584a3212f233b655f87b89f22f1d4890782447c4ef742b37af58d + + uint256[10] public forks; + + function setUp() public { + compoundUniTrap = new CompoundUniTrap(); + + for (uint256 i = 0; i < 10; i++) { + console.log("Setting for block number: ",currentBlockNumber + i); + forks[i] = vm.createSelectFork(vm.rpcUrl("https://mainnet.infura.io/v3/39b2abffe10e4659a12074ce9a344bae"), currentBlockNumber + i); + } + } + + function testContinuousMonitoring() public { + for (uint256 i = 0; i < 10; i++) { + console.log("Block Number:",currentBlockNumber + forks[i]); + CompoundUniTrap.PriceDataPoint[] memory prices = new CompoundUniTrap.PriceDataPoint[](1); + vm.selectFork(forks[i]); + + prices[0] = compoundUniTrap.collect(); + + bool valid = compoundUniTrap.isValid(prices); + if (!valid) { + console.log("Price deviation detected at block: ", currentBlockNumber + i); + break; + } + } + } +} \ No newline at end of file