Skip to content
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
72 changes: 72 additions & 0 deletions lending-protocols/aave/README.md
Original file line number Diff line number Diff line change
@@ -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.


6 changes: 6 additions & 0 deletions lending-protocols/aave/foundry.toml
Original file line number Diff line number Diff line change
@@ -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
48 changes: 48 additions & 0 deletions lending-protocols/aave/src/AaveFlashLoanTrap.sol
Original file line number Diff line number Diff line change
@@ -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;
}
}
48 changes: 48 additions & 0 deletions lending-protocols/aave/src/AaveLikeProtocol.sol
Original file line number Diff line number Diff line change
@@ -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;
}
}
58 changes: 58 additions & 0 deletions lending-protocols/aave/test/AaveFlashLoanTrapTest.t.sol
Original file line number Diff line number Diff line change
@@ -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");
}
}