diff --git a/lending-protocols/aave/README.md b/lending-protocols/aave/README.md new file mode 100644 index 0000000..f8d3124 --- /dev/null +++ b/lending-protocols/aave/README.md @@ -0,0 +1,72 @@ +## Introduction +In decentralized finance (DeFi), flash loan exploits can significantly affect lending platforms. These attacks exploit vulnerabilities to manipulate liquidity pools and profit from arbitrage, leading to significant financial loss and instability in the protocol. This demo shows how an incident involving a flash loan exploit by a lending protocol like Aave and how a trap contract [AaveFlashLoanTrap](https://github.com/surbhit14/trap-contract-sample-aave/blob/master/src/AaveFlashLoanTrap.sol) can help detect and mitigate such attacks using an automated pause functionality + +## Incident Overview +A flash loan exploit involves borrowing a large amount of liquidity from a protocol without collateral, exploiting vulnerabilities in the protocol to profit from arbitrage, and then repaying the loan within the same transaction. This can lead to drastic changes in liquidity and manipulate asset prices. + +## Aave Flash Loan Trap + +### Concept +The AaveFlashLoanTrap is designed to monitor lending platforms for significant changes in liquidity that could indicate a flash loan exploit. By collecting and analyzing liquidity data, it can detect abnormal changes and trigger a pause in the protocol to prevent further damage. + +## How the Trap Detects and Prevents Attacks +### Data Collection: +The trap contract periodically collects liquidity data from the Aave-like protocol. This data is crucial for detecting any sudden changes in the protocol's state. +### Validation: +The collected data points are compared to detect significant drops in liquidity. If a decrease greater than the predefined threshold (10% in this case) is detected between two consecutive data points, it indicates a potential attack. +### Response: +If an anomaly is detected, the isValid function triggers the protocol's pause function. This halts all operations, preventing further exploitation and allowing the protocol's developers to investigate and mitigate the issue. + +## The Importance of Pause Functionality +The inclusion of a pause functionality is critical for the following reasons: + +### Immediate Response: +To integrate this trap into any Aave-like protocol, the protocol should implement a pause function that can be called by the trap contract upon detecting an anomaly +Automatically pausing the protocol upon detecting an attack prevents further damage. +### Investigation and Mitigation: +Pausing operations gives developers time to investigate the cause and apply necessary fixes without the risk of ongoing exploitation. +### Community Trust: +Demonstrates a proactive approach to security, helping maintain user trust in the protocol. + +## Running the Examples +``` +forge test +``` + +## Test Results +``` +Ran 3 tests for test/AaveFlashLoanTrapTest.t.sol:AaveFlashLoanTrapTest +[PASS] testCollect() (gas: 19463) +Logs: + Setup completed: AaveLikeProtocol deployed with liquidity 1000 and AaveFlashLoanTrap deployed. + Collected available liquidity: 1000 + +[PASS] testIsValid() (gas: 35922) +Logs: + Setup completed: AaveLikeProtocol deployed with liquidity 1000 and AaveFlashLoanTrap deployed. + Data points set: [1000, 850] + Calling isValid... + decreasePercentage: 15 + isValid result: false + Checking if protocol is paused... + Protocol paused: true + +[PASS] testIsValidWithNoPause() (gas: 28137) +Logs: + Setup completed: AaveLikeProtocol deployed with liquidity 1000 and AaveFlashLoanTrap deployed. + Data points set: [1000, 950] + Calling isValid... + decreasePercentage: 5 + isValid result: true + Checking if protocol is paused... + Protocol paused: false + +Suite result: ok. 3 passed; 0 failed; 0 skipped; finished in 2.26ms (1.29ms CPU time) +``` +## Conclusion + +Flash loan exploits pose a significant threat to the stability and security of decentralized lending platforms. By employing the AaveFlashLoanTrap, protocols can detect abnormal liquidity changes indicative of such attacks. This trap operates by periodically collecting liquidity data, validating it against predefined thresholds, and triggering an automatic pause when suspicious activity is detected. + +Integrating the pause functionality into the protocol is crucial for immediate response, allowing developers to investigate and mitigate the exploit while preventing further damage. This proactive approach not only helps in maintaining the integrity of the protocol but also preserves user trust and confidence. + + diff --git a/lending-protocols/aave/foundry.toml b/lending-protocols/aave/foundry.toml new file mode 100644 index 0000000..25b918f --- /dev/null +++ b/lending-protocols/aave/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/lending-protocols/aave/src/AaveFlashLoanTrap.sol b/lending-protocols/aave/src/AaveFlashLoanTrap.sol new file mode 100644 index 0000000..15712b0 --- /dev/null +++ b/lending-protocols/aave/src/AaveFlashLoanTrap.sol @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; +import "forge-std/console.sol"; + +interface IAaveLikeProtocol { + function availableLiquidity() external view returns (uint256); + function pause() external; +} + +contract AaveFlashLoanTrap { + struct LiquidityInfo { + uint256 availableLiquidity; + } + + IAaveLikeProtocol public protocol; + address public owner; + + constructor(address _protocol) { + protocol = IAaveLikeProtocol(_protocol); + owner = msg.sender; + } + + modifier onlyOwner() { + require(msg.sender == owner, "Not authorized"); + _; + } + + + function collect() external view returns (LiquidityInfo memory) { + uint256 availableLiquidity = protocol.availableLiquidity(); + return LiquidityInfo({availableLiquidity: availableLiquidity}); + } + + function isValid(LiquidityInfo[] calldata dataPoints) external onlyOwner returns (bool) { + if (dataPoints.length < 2) { + return true; + } + + uint256 liquidityDecrease = dataPoints[0].availableLiquidity - dataPoints[1].availableLiquidity; + uint256 decreasePercentage = (liquidityDecrease * 100) / dataPoints[0].availableLiquidity; + console.log("decreasePercentage: ",decreasePercentage); + if (decreasePercentage > 10) { + protocol.pause(); + return false; + } + return true; + } +} diff --git a/lending-protocols/aave/src/AaveLikeProtocol.sol b/lending-protocols/aave/src/AaveLikeProtocol.sol new file mode 100644 index 0000000..7e9005e --- /dev/null +++ b/lending-protocols/aave/src/AaveLikeProtocol.sol @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import "./AaveFlashLoanTrap.sol"; + +contract AaveLikeProtocol { + uint256 public availableLiquidityValue; + bool public paused; + address public owner; + + + constructor(address _owner) { + owner = _owner; + paused = false; + } + + modifier onlyOwner() { + require(msg.sender == owner, "Not authorized"); + _; + } + + modifier onlyTrapContract() { + require(msg.sender == address(trapContract), "Not authorized"); + _; + } + + AaveFlashLoanTrap public trapContract; + + function setTrapContract(AaveFlashLoanTrap _trapContract) external { + trapContract = _trapContract; + } + + function setLiquidity(uint256 _liquidity) external onlyOwner { + availableLiquidityValue = _liquidity; + } + + function availableLiquidity() external view returns (uint256) { + return availableLiquidityValue; + } + + function pause() external onlyTrapContract { + paused = true; + } + + function unpause() external onlyTrapContract { + paused = false; + } +} diff --git a/lending-protocols/aave/test/AaveFlashLoanTrapTest.t.sol b/lending-protocols/aave/test/AaveFlashLoanTrapTest.t.sol new file mode 100644 index 0000000..4890f2a --- /dev/null +++ b/lending-protocols/aave/test/AaveFlashLoanTrapTest.t.sol @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import "forge-std/Test.sol"; +import "forge-std/console.sol"; +import "../src/AaveFlashLoanTrap.sol"; +import "../src/AaveLikeProtocol.sol"; + +contract AaveFlashLoanTrapTest is Test { + AaveLikeProtocol public aave; + AaveFlashLoanTrap public trap; + + function setUp() public { + aave = new AaveLikeProtocol(address(this)); // Deploy the Aave-like protocol + trap = new AaveFlashLoanTrap(address(aave)); // Deploy the trap contract + aave.setLiquidity(1000); // Initialize the protocol with some liquidity + aave.setTrapContract(trap); + console.log("Setup completed: AaveLikeProtocol deployed with liquidity 1000 and AaveFlashLoanTrap deployed."); + } + + function testCollect() public view { + AaveFlashLoanTrap.LiquidityInfo memory data = trap.collect(); + console.log("Collected available liquidity:", data.availableLiquidity); + assertGt(data.availableLiquidity, 0, "Available liquidity should be greater than 0"); + } + + function testIsValid() public { + AaveFlashLoanTrap.LiquidityInfo[] memory dataPoints = new AaveFlashLoanTrap.LiquidityInfo[](2); + dataPoints[0] = AaveFlashLoanTrap.LiquidityInfo({availableLiquidity: 1000}); + dataPoints[1] = AaveFlashLoanTrap.LiquidityInfo({availableLiquidity: 850}); + + console.log("Data points set: [1000, 850]"); + console.log("Calling isValid..."); + bool result = trap.isValid(dataPoints); + console.log("isValid result:", result); + + console.log("Checking if protocol is paused..."); + assertEq(result, false, "Liquidity decrease more than 10% should be invalid"); + console.log("Protocol paused: ",aave.paused()); + assertEq(aave.paused(), true, "Protocol should be paused"); + } + + function testIsValidWithNoPause() public { + AaveFlashLoanTrap.LiquidityInfo[] memory dataPoints = new AaveFlashLoanTrap.LiquidityInfo[](2); + dataPoints[0] = AaveFlashLoanTrap.LiquidityInfo({availableLiquidity: 1000}); + dataPoints[1] = AaveFlashLoanTrap.LiquidityInfo({availableLiquidity: 950}); + + console.log("Data points set: [1000, 950]"); + console.log("Calling isValid..."); + bool result = trap.isValid(dataPoints); + console.log("isValid result:", result); + + console.log("Checking if protocol is paused..."); + assertEq(result, true, "No significant liquidity decrease should be valid"); + console.log("Protocol paused: ",aave.paused()); + assertEq(aave.paused(), false, "Protocol should not be paused"); + } +}