Skip to content

Commit

Permalink
feat: honeypotdao and deployment script (#5)
Browse files Browse the repository at this point in the history
Signed-off-by: Evaldo Felipe <[email protected]>
Signed-off-by: Pablo Maldonado <[email protected]>
Co-authored-by: Evaldo Felipe <[email protected]>
  • Loading branch information
md0x and evaldofelipe authored Oct 30, 2023
1 parent ccf1f9b commit 3ee5280
Show file tree
Hide file tree
Showing 6 changed files with 197 additions and 9 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
cache/
out/
broadcast/
39 changes: 32 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

# <h1 align="center"> OEV Share HoneyPot Demo </h1>

**This repository is a demonstration of the OEV Share system and a HoneyPot mechanism. It showcases how a backrunner can liquidate a position, in this particular case, how a HoneyPot can be emptied given a specific price change.**
Expand All @@ -12,16 +11,42 @@ The HoneyPot mechanism is a unique setup where funds are kept in a contract that
## Getting Started

To test the demo run the following commands:

```
forge install
export RPC_MAINNET=https://mainnet.infura.io/v3/<YOUR_INFURA_KEY>
forge test`
forge test`
```

## Contracts Overview

- **HoneyPot**: Represents the honey pot, which can be emptied when a price oracle returns a value different from a pre-defined liquidation price. The honey pot's funds can also be reset by its owner.

- **HoneyPotOEVShare**: Acts as the oracle which retrieves prices from various sources like Chainlink, Chronicle, and Pyth.

- **Test Contract**: Sets up the environment, including simulating price changes and testing the mechanisms for creating and emptying the HoneyPot.
- **HoneyPot**: Represents the honey pot, which can be emptied when a price oracle returns a value different from a pre-defined liquidation price. The honey pot's funds can also be reset by its owner.
- **HoneyPotOEVShare**: Acts as the oracle which retrieves prices from various sources like Chainlink, Chronicle, and Pyth.
- **Test Contract**: Sets up the environment, including simulating price changes and testing the mechanisms for creating and emptying the HoneyPot.

## Deploy the Contracts

Can be run against a fork with anvil:

```bash
anvil --fork-url https://mainnet.infura.io/v3/<YOUR_KEY>
```

Then:

```bash
export MNEMONIC="test test test test test test test test test test test junk"
export DEPLOYER_WALLET=$(cast wallet address --mnemonic "$MNEMONIC")
export ETH_RPC_URL="http://127.0.0.1:8545"

# The following variables can be skipped if you want to use the default values
export CHAINLINK_SOURCE = "0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419" // chosen from https://docs.chain.link/docs/reference-contracts
export PYTH_SOURCE = "0x4305FB66699C3B2702D4d05CF36551390A4c69C6" // chosen from https://pyth.network/markets
export PYTH_PRICE_ID = "0xff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace" // chosen from https://pyth.network/markets

forge script script/HoneyPot.s.sol \
--fork-url $ETH_RPC_URL \
--mnemonics "$MNEMONIC" \
--sender $DEPLOYER_WALLET \
--broadcast
```
49 changes: 49 additions & 0 deletions script/HoneyPot.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.0;

import "forge-std/console2.sol";
import "forge-std/Script.sol";

import {ChronicleMedianSourceMock} from "../src/mock/ChronicleMedianSourceMock.sol";
import {IMedian} from "oev-contracts/interfaces/chronicle/IMedian.sol";
import {HoneyPotOEVShare} from "../src/HoneyPotOEVShare.sol";
import {HoneyPot} from "../src/HoneyPot.sol";
import {HoneyPotDAO} from "../src/HoneyPotDAO.sol";
import {IAggregatorV3Source} from "oev-contracts/interfaces/chainlink/IAggregatorV3Source.sol";

contract HoneyPotDeploymentScript is Script {
HoneyPotOEVShare oevShare;
HoneyPot honeyPot;
HoneyPotDAO honeyPotDAO;
ChronicleMedianSourceMock chronicleMock;

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

address chainlink = vm.envOr("CHAINLINK_SOURCE", 0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419);
address pyth = vm.envOr("PYTH_SOURCE", 0x4305FB66699C3B2702D4d05CF36551390A4c69C6);

bytes32 defaultId = 0xff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace;
bytes32 pythPriceId = vm.envOr("PYTH_PRICE_ID", bytes32(0));
if (pythPriceId == bytes32(0)) {
pythPriceId = defaultId;
}

// Create mock ChronicleMedianSource and set the latest source data.
chronicleMock = new ChronicleMedianSourceMock();

oevShare = new HoneyPotOEVShare(
chainlink,
address(chronicleMock),
pyth,
pythPriceId,
8
);

honeyPot = new HoneyPot(IAggregatorV3Source(address(oevShare)));

honeyPotDAO = new HoneyPotDAO();

vm.stopBroadcast();
}
}
21 changes: 21 additions & 0 deletions src/HoneyPotDAO.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.17;

import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {Address} from "@openzeppelin/contracts/utils/Address.sol";

contract HoneyPotDAO is Ownable {
// Define events
event ReceivedEther(address sender, uint256 amount);
event DrainedEther(address to, uint256 amount);

receive() external payable {
emit ReceivedEther(msg.sender, msg.value);
}

function drain() external onlyOwner {
uint256 balance = address(this).balance;
Address.sendValue(payable(owner()), balance);
emit DrainedEther(owner(), balance);
}
}
29 changes: 29 additions & 0 deletions src/mock/ChronicleMedianSourceMock.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.17;

import {IMedian} from "oev-contracts/interfaces/chronicle/IMedian.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";

contract ChronicleMedianSourceMock is IMedian, Ownable {
uint256 public value;
uint32 public ageValue;

function age() external view returns (uint32) {
return ageValue;
}

function read() external view returns (uint256) {
return value;
}

function peek() external view returns (uint256, bool) {
return (value, true);
}

function setLatestSourceData(uint256 _value, uint32 _age) public onlyOwner {
value = _value;
ageValue = _age;
}

function kiss(address) external override {}
}
67 changes: 65 additions & 2 deletions test/HoneyPot.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,23 @@ import {IPyth} from "oev-contracts/interfaces/pyth/IPyth.sol";

import {HoneyPotOEVShare} from "../src/HoneyPotOEVShare.sol";
import {HoneyPot} from "../src/HoneyPot.sol";
import {HoneyPotDAO} from "../src/HoneyPotDAO.sol";
import {ChronicleMedianSourceMock} from "../src/mock/ChronicleMedianSourceMock.sol";

contract HoneyPotTest is CommonTest {
event ReceivedEther(address sender, uint256 amount);
event DrainedEther(address to, uint256 amount);

IAggregatorV3Source chainlink = IAggregatorV3Source(0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419);
IMedian chronicle = IMedian(0x64DE91F5A373Cd4c28de3600cB34C7C6cE410C85);
IPyth pyth = IPyth(0x4305FB66699C3B2702D4d05CF36551390A4c69C6);
bytes32 pythPriceId = 0xff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace;

ChronicleMedianSourceMock chronicleMock;

HoneyPotOEVShare oevShare;
HoneyPot honeyPot;
HoneyPotDAO honeyPotDAO;

uint256 public constant liquidationPrice = 0.1e18;
uint256 public honeyPotBalance = 1 ether;
Expand All @@ -32,8 +40,10 @@ contract HoneyPotTest is CommonTest {
);

honeyPot = new HoneyPot(IAggregatorV3Source(address(oevShare)));
honeyPotDAO = new HoneyPotDAO();
_whitelistOnChronicle();
oevShare.setUnlocker(address(this), true);
chronicleMock = new ChronicleMedianSourceMock();
}

receive() external payable {}
Expand Down Expand Up @@ -86,7 +96,7 @@ contract HoneyPotTest is CommonTest {

vm.prank(liquidator);
vm.expectRevert("Liquidation price reached for this user");
honeyPot.emptyHoneyPot(address(this)); // emptyHoneyPot now requires the creator's address
honeyPot.emptyHoneyPot(address(this));

// Simulate price change
mockChainlinkPriceChange();
Expand All @@ -97,7 +107,7 @@ contract HoneyPotTest is CommonTest {
uint256 liquidatorBalanceBefore = liquidator.balance;

vm.prank(liquidator);
honeyPot.emptyHoneyPot(address(this)); // emptyHoneyPot now requires the creator's address
honeyPot.emptyHoneyPot(address(this));

uint256 liquidatorBalanceAfter = liquidator.balance;

Expand All @@ -108,4 +118,57 @@ contract HoneyPotTest is CommonTest {
(, uint256 testhoneyPotBalanceTwo) = honeyPot.honeyPots(address(this));
assertTrue(testhoneyPotBalanceTwo == honeyPotBalance);
}

function testHoneyPotDAO() public {
vm.expectEmit(true, true, true, true);
emit ReceivedEther(address(this), 1 ether);
payable(address(honeyPotDAO)).transfer(1 ether);

vm.expectEmit(true, true, true, true);
emit DrainedEther(address(this), 1 ether);
honeyPotDAO.drain();
}

function testChronicleMock() public {
uint32 age = chronicle.age();
uint256 read = chronicle.read();
chronicleMock.setLatestSourceData(read, age);

HoneyPotOEVShare oevShare2 = new HoneyPotOEVShare(
address(chainlink),
address(chronicleMock),
address(pyth),
pythPriceId,
8
);
oevShare2.setUnlocker(address(this), true);

HoneyPot honeyPot2 = new HoneyPot(
IAggregatorV3Source(address(oevShare2))
);

// Create HoneyPot for the caller
honeyPot2.createHoneyPot{value: honeyPotBalance}();
(, uint256 testhoneyPotBalance) = honeyPot2.honeyPots(address(this));
assertTrue(testhoneyPotBalance == honeyPotBalance);

vm.prank(liquidator);
vm.expectRevert("Liquidation price reached for this user");
honeyPot2.emptyHoneyPot(address(this));

// Simulate price change
chronicleMock.setLatestSourceData((read * 103) / 100, uint32(block.timestamp - 1));

// Unlock the latest value
oevShare2.unlockLatestValue();

uint256 liquidatorBalanceBefore = liquidator.balance;

vm.prank(liquidator);
honeyPot2.emptyHoneyPot(address(this));

uint256 liquidatorBalanceAfter = liquidator.balance;

assertTrue(liquidatorBalanceAfter == liquidatorBalanceBefore + honeyPotBalance);
}
}

0 comments on commit 3ee5280

Please sign in to comment.