Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: redstone oev #436

Closed
wants to merge 11 commits into from
Closed
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
9 changes: 9 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,12 @@
[submodule "lib/solady"]
path = lib/solady
url = https://github.com/vectorized/solady
[submodule "lib/redstone-oracles-monorepo"]
path = lib/redstone-oracles-monorepo
url = https://github.com/redstone-finance/redstone-oracles-monorepo
[submodule "lib/openzeppelin-contracts-upgradeable"]
path = lib/openzeppelin-contracts-upgradeable
url = https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable
[submodule "lib/chainlink"]
path = lib/chainlink
url = https://github.com/smartcontractkit/chainlink
1 change: 1 addition & 0 deletions lib/chainlink
Submodule chainlink added at 72a7d2
2 changes: 1 addition & 1 deletion lib/forge-std
Submodule forge-std updated 2 files
+104 −0 src/StdJson.sol
+104 −0 src/StdToml.sol
1 change: 1 addition & 0 deletions lib/openzeppelin-contracts-upgradeable
1 change: 1 addition & 0 deletions lib/redstone-oracles-monorepo
2 changes: 1 addition & 1 deletion lib/solady
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
"deploy-atlas-bsc": "source .env && forge script script/deploy-atlas.s.sol:DeployAtlasScript --rpc-url ${BSC_RPC_URL} --legacy --broadcast --etherscan-api-key ${BSCSCAN_API_KEY} --verify --delay 30",
"deploy-atlas-bsc-testnet": "source .env && forge script script/deploy-atlas.s.sol:DeployAtlasScript --rpc-url ${BSC_TESTNET_RPC_URL} --legacy --broadcast --etherscan-api-key ${BSCSCAN_API_KEY} --verify --delay 30",

"deploy-redstone-oev-dapp-bsc-testnet": "source .env && forge script script/deploy-redstone-oev-control.s.sol:DeployRedstoneOevControlScript --rpc-url ${BSC_TESTNET_RPC_URL} --legacy --broadcast --etherscan-api-key ${BSCSCAN_API_KEY} --verify --delay 30",

"deploy-atlas-sepolia": "source .env && forge script script/deploy-atlas.s.sol:DeployAtlasScript --rpc-url ${SEPOLIA_RPC_URL} --legacy --gas-estimate-multiplier 150 --broadcast --etherscan-api-key ${ETHERSCAN_API_KEY} --verify --delay 30",
"deploy-atlas-local": "source .env && forge script script/deploy-atlas.s.sol:DeployAtlasScript --fork-url http://localhost:8545 --broadcast",

Expand Down
5 changes: 4 additions & 1 deletion remappings.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,7 @@ forge-std/=lib/forge-std/src/
hardhat/=node_modules/hardhat/
openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/
openzeppelin-contracts/=lib/openzeppelin-contracts/
solady/=lib/solady/src/
@openzeppelin/contracts=lib/openzeppelin-contracts/contracts/
solady/=lib/solady/src/
@redstone-finance/evm-connector/contracts/=lib/redstone-oracles-monorepo/packages/evm-connector/contracts/
@chainlink/contracts/=lib/chainlink/contracts/
10 changes: 5 additions & 5 deletions script/base/deploy-base.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -52,19 +52,19 @@ contract DeployBaseScript is Script {
} else if (chainId == 56) {
return "BSC";
} else if (chainId == 97) {
return "BSC TESTNET";
return "BSC_TESTNET";
} else if (chainId == 10) {
return "OP MAINNET";
return "OP_MAINNET";
} else if (chainId == 11_155_420) {
return "OP SEPOLIA";
return "OP_SEPOLIA";
} else if (chainId == 42_161) {
return "ARBITRUM";
} else if (chainId == 421_614) {
return "ARBITRUM SEPOLIA";
return "ARBITRUM_SEPOLIA";
} else if (chainId == 8453) {
return "BASE";
} else if (chainId == 84_532) {
return "BASE SEPOLIA";
return "BASE_SEPOLIA";
} else {
revert("Error: Chain ID not recognized");
}
Expand Down
104 changes: 104 additions & 0 deletions script/deploy-redstone-demo.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.25;

import "forge-std/Script.sol";
import "forge-std/Test.sol";
import { DeployBaseScript } from "script/base/deploy-base.s.sol";
import { RedstoneDAppControl } from "src/contracts/examples/redstone-oev/RedstoneDAppControl.sol";
import { RedstoneAdapterAtlasWrapper } from "src/contracts/examples/redstone-oev/RedstoneAdapterAtlasWrapper.sol";
import { MockBaseFeed } from "src/contracts/examples/redstone-oev/MockBaseFeed.sol";
import { Atlas } from "src/contracts/atlas/Atlas.sol";
import { AtlasVerification } from "src/contracts/atlas/AtlasVerification.sol";
import { HoneyPot } from "src/contracts/examples/redstone-oev/HoneyPot.sol";

contract DeployRedstoneDemoScript is DeployBaseScript {
RedstoneDAppControl redstoneDAppControl;
address redstoneExecutionEnv;
address redstoneAdapterAtlasWrapper;

function run() external {
console.log("\n=== DEPLOYING REDSTONE DEMO ===\n");

uint256 govPrivateKey = vm.envUint("DAPP_GOV_PRIVATE_KEY");
address gov = vm.addr(govPrivateKey);

console.log("Redstone Gov address: \t\t\t", gov);

uint256 oracleOwnerPrivateKey = vm.envUint("ORACLE_OWNER_PRIVATE_KEY");
address oracleOwner = vm.addr(oracleOwnerPrivateKey);
address baseFeedAddress = vm.envAddress("BASE_FEED_ADDRESS");

atlas = Atlas(payable(vm.envAddress("ATLAS_ADDRESS")));
atlasVerification = AtlasVerification(payable(vm.envAddress("ATLAS_VERIFICATION_ADDRESS")));

console.log("Using Atlas deployed at: \t\t\t", address(atlas));
console.log("Using AtlasVerification deployed at: \t\t", address(atlasVerification));
console.log("\n");

vm.startBroadcast(govPrivateKey);
console.log("Deploying from Gov Account...");

redstoneDAppControl = new RedstoneDAppControl(address(atlas));
atlasVerification.initializeGovernance(address(redstoneDAppControl));

vm.stopBroadcast();
console.log("Contracts deployed by Gov:");
console.log("Redstone DAppControl: \t\t\t", address(redstoneDAppControl));
console.log("\n");

console.log("Oracle Owner address: \t\t\t", oracleOwner);
console.log("Base Feed address: \t\t\t\t", baseFeedAddress);

vm.startBroadcast(oracleOwnerPrivateKey);
console.log("Deploying from Oracle Owner Account...");
redstoneAdapterAtlasWrapper = redstoneDAppControl.createNewAtlasAdapter(baseFeedAddress);
vm.stopBroadcast();

console.log("Contracts deployed by Oracle Owner:");
console.log("Redstone Adapter Atlas Wrapper: \t\t\t", redstoneAdapterAtlasWrapper);
console.log("\n");
}
}

contract DeployRedstoneHoneyPot is DeployBaseScript {
function run() external {
console.log("\n=== DEPLOYING REDSTONE HONEY POT ===\n");

uint256 honeyPotPrivateKey = vm.envUint("HONEYPOT_OWNER_PRIVATE_KEY");
address honeyPotOwner = vm.addr(honeyPotPrivateKey);

address oracle = vm.envAddress("ORACLE_ADDRESS");

console.log("Honey Pot Owner address: \t\t", honeyPotOwner);
console.log("Oracle address: \t\t\t", oracle);

vm.startBroadcast(honeyPotPrivateKey);
console.log("Deploying from Honey Pot Owner Account...");

HoneyPot honeyPot = new HoneyPot(honeyPotOwner, oracle);

vm.stopBroadcast();
console.log("Contracts deployed by Honey Pot Owner:");
console.log("Honey Pot: \t\t\t\t", address(honeyPot));
console.log("\n");
}
}

contract DeployRedstoneMockBaseFeed is DeployBaseScript {
function run() external {
console.log("\n=== DEPLOYING REDSTONE MOCK BASE FEED ===\n");

uint256 authorizedSignerPrivateKey = vm.envUint("BASE_FEED_OWNER");
address authorizedSigner = vm.addr(authorizedSignerPrivateKey);

console.log("Authorized Signer address: \t\t", authorizedSigner);

vm.startBroadcast(authorizedSignerPrivateKey);
console.log("Deploying from Authorized Signer Account...");
MockBaseFeed mockBaseFeed = new MockBaseFeed(authorizedSigner);
vm.stopBroadcast();

console.log("Contracts deployed by Authorized Signer:");
console.log("Mock Base Feed: \t\t\t", address(mockBaseFeed));
}
}
48 changes: 48 additions & 0 deletions script/deploy-redstone-oev-control.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.25;

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

import { DeployBaseScript } from "script/base/deploy-base.s.sol";
import { RedstoneOevDAppControl } from "src/contracts/examples/redstone-oev/RedstoneOevDAppControl.sol";
import { AtlasVerification } from "src/contracts/atlas/AtlasVerification.sol";

contract DeployRedstoneOevControlScript is DeployBaseScript {
RedstoneOevDAppControl redstoneDAppControl;

function run() external {
console.log("\n=== DEPLOYING REDSTONE OEV CONTROL ===\n");

uint256 deployerPrivateKey = vm.envUint("GOV_PRIVATE_KEY");
address deployer = vm.addr(deployerPrivateKey);

console.log("Deployer address: \t\t\t\t", deployer);

address atlasAddress = _getAddressFromDeploymentsJson("ATLAS");
address atlasVerificationAddress = _getAddressFromDeploymentsJson("ATLAS_VERIFICATION");

console.log("Using Atlas deployed at: \t\t\t", atlasAddress);
console.log("Using Atlas Verification deployed at: \t", atlasVerificationAddress);
console.log("\n");

console.log("Deploying from deployer Account...");

vm.startBroadcast(deployerPrivateKey);

redstoneDAppControl = new RedstoneOevDAppControl(
atlasAddress,
0, // Bundler share
1000, // Fastlane share (10%)
deployer // Allocation destination
);

AtlasVerification(atlasVerificationAddress).initializeGovernance(address(redstoneDAppControl));

vm.stopBroadcast();

console.log("Contracts deployed by deployer:");
console.log("Redstone OEV DAppControl: \t\t\t", address(redstoneDAppControl));
console.log("\n");
}
}
75 changes: 75 additions & 0 deletions src/contracts/examples/redstone-oev/HoneyPot.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.25;

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

interface IFeed {
function latestRoundData()
external
view
returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);
}

contract HoneyPot is Ownable {
struct HoneyPotDetails {
int256 liquidationPrice;
uint256 balance;
}

mapping(address => HoneyPotDetails) public honeyPots;
address public oracle; // Oval serving as a Chainlink oracle

event OracleUpdated(address indexed newOracle);
event HoneyPotCreated(address indexed owner, int256 initialPrice, uint256 amount);
event HoneyPotEmptied(address indexed owner, address indexed liquidator, uint256 amount);
event HoneyPotReset(address indexed owner, uint256 amount);

constructor(address _owner, address _oracle) Ownable(_owner) {
oracle = _oracle;
}

function setOracle(address _oracle) external onlyOwner {
oracle = _oracle;
emit OracleUpdated(address(_oracle));
}

function createHoneyPot() external payable {
require(honeyPots[msg.sender].liquidationPrice == 0, "Liquidation price already set for this user");
require(msg.value > 0, "No value sent");

(, int256 currentPrice,,,) = IFeed(oracle).latestRoundData();

honeyPots[msg.sender].liquidationPrice = currentPrice;
honeyPots[msg.sender].balance = msg.value;

emit HoneyPotCreated(msg.sender, currentPrice, msg.value);
}

function _emptyPotForUser(address user, address recipient) internal returns (uint256 amount) {
HoneyPotDetails storage userPot = honeyPots[user];

amount = userPot.balance;
userPot.balance = 0; // reset the balance
userPot.liquidationPrice = 0; // reset the liquidation price
Address.sendValue(payable(recipient), amount);
}

function emptyHoneyPot(address user) external {
(, int256 currentPrice,,,) = IFeed(oracle).latestRoundData();
require(currentPrice >= 0, "Invalid price from oracle");

HoneyPotDetails storage userPot = honeyPots[user];

require(currentPrice != userPot.liquidationPrice, "Liquidation price reached for this user");
require(userPot.balance > 0, "No balance to withdraw");

uint256 withdrawnAmount = _emptyPotForUser(user, msg.sender);
emit HoneyPotEmptied(user, msg.sender, withdrawnAmount);
}

function resetPot() external {
uint256 withdrawnAmount = _emptyPotForUser(msg.sender, msg.sender);
emit HoneyPotReset(msg.sender, withdrawnAmount);
}
}
26 changes: 26 additions & 0 deletions src/contracts/examples/redstone-oev/MockBaseFeed.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.25;

import { MergedPriceFeedAdapterWithRounds } from
"lib/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/with-rounds/MergedPriceFeedAdapterWithRounds.sol";

contract MockBaseFeed is MergedPriceFeedAdapterWithRounds {
error UnauthorizedSigner();

address public AUTHORIZED_SIGNER;

constructor(address authorizedSigner) {
AUTHORIZED_SIGNER = authorizedSigner;
}

function getAuthorisedSignerIndex(address receivedSigner) public view virtual override returns (uint8) {
if (receivedSigner == AUTHORIZED_SIGNER) {
return 0;
}
revert UnauthorizedSigner();
}

function getDataFeedId() public view virtual override returns (bytes32) {
return bytes32("ATLAS-DEMO-MOCK-FEED");
}
}
Loading
Loading