Files
strategic/contracts/AtomicExecutor.sol

196 lines
5.7 KiB
Solidity
Raw Permalink Normal View History

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/security/Pausable.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
interface IPool {
function flashLoanSimple(
address receiverAddress,
address asset,
uint256 amount,
bytes calldata params,
uint16 referralCode
) external;
}
interface IFlashLoanSimpleReceiver {
function executeOperation(
address asset,
uint256 amount,
uint256 premium,
bytes calldata params
) external returns (bool);
}
/**
* @title AtomicExecutor
* @notice Executes batches of calls atomically, with optional flash loan support
*/
contract AtomicExecutor is Ownable, Pausable, ReentrancyGuard {
mapping(address => bool) public allowedTargets;
mapping(address => bool) public allowedPools; // Aave pools that can call flash loan callback
bool public allowListEnabled;
event TargetAllowed(address indexed target, bool allowed);
event PoolAllowed(address indexed pool, bool allowed);
event BatchExecuted(address indexed caller, uint256 callCount);
event FlashLoanExecuted(address indexed asset, uint256 amount);
constructor(address _owner) Ownable(_owner) {
allowListEnabled = true;
}
/**
* @notice Enable/disable allow list
*/
function setAllowListEnabled(bool _enabled) external onlyOwner {
allowListEnabled = _enabled;
}
/**
* @notice Allow/deny a target address
*/
function setAllowedTarget(address _target, bool _allowed) external onlyOwner {
allowedTargets[_target] = _allowed;
emit TargetAllowed(_target, _allowed);
}
/**
* @notice Batch allow/deny multiple targets
*/
function setAllowedTargets(address[] calldata _targets, bool _allowed) external onlyOwner {
for (uint256 i = 0; i < _targets.length; i++) {
allowedTargets[_targets[i]] = _allowed;
emit TargetAllowed(_targets[i], _allowed);
}
}
/**
* @notice Execute a batch of calls atomically
* @param targets Array of target addresses
* @param calldatas Array of calldata for each call
*/
function executeBatch(
address[] calldata targets,
bytes[] calldata calldatas
) external whenNotPaused nonReentrant {
require(targets.length == calldatas.length, "Length mismatch");
require(targets.length > 0, "Empty batch");
for (uint256 i = 0; i < targets.length; i++) {
if (allowListEnabled) {
require(allowedTargets[targets[i]], "Target not allowed");
}
(bool success, bytes memory returnData) = targets[i].call(calldatas[i]);
require(success, string(returnData));
}
emit BatchExecuted(msg.sender, targets.length);
}
/**
* @notice Execute a flash loan and callback
* @param pool Aave v3 Pool address
* @param asset Asset to borrow
* @param amount Amount to borrow
* @param targets Array of target addresses for callback
* @param calldatas Array of calldata for callback
*/
function executeFlashLoan(
address pool,
address asset,
uint256 amount,
address[] calldata targets,
bytes[] calldata calldatas
) external whenNotPaused nonReentrant {
require(targets.length == calldatas.length, "Length mismatch");
bytes memory params = abi.encode(targets, calldatas, msg.sender);
IPool(pool).flashLoanSimple(
address(this),
asset,
amount,
params,
0
);
emit FlashLoanExecuted(asset, amount);
}
/**
* @notice Allow/deny a pool address for flash loan callbacks
*/
function setAllowedPool(address _pool, bool _allowed) external onlyOwner {
allowedPools[_pool] = _allowed;
emit PoolAllowed(_pool, _allowed);
}
/**
* @notice Flash loan callback (called by Aave Pool)
*/
function executeOperation(
address asset,
uint256 amount,
uint256 premium,
bytes calldata params
) external returns (bool) {
// Verify caller is an allowed Aave Pool
require(allowedPools[msg.sender], "Unauthorized pool");
// Decode params
(address[] memory targets, bytes[] memory calldatas, address initiator) = abi.decode(
params,
(address[], bytes[], address)
);
// Verify initiator is authorized (optional check)
require(initiator == tx.origin || initiator == address(this), "Unauthorized initiator");
// Execute callback operations
for (uint256 i = 0; i < targets.length; i++) {
if (allowListEnabled) {
require(allowedTargets[targets[i]], "Target not allowed");
}
(bool success, bytes memory returnData) = targets[i].call(calldatas[i]);
require(success, string(returnData));
}
// Approve repayment
IERC20(asset).approve(msg.sender, amount + premium);
return true;
}
/**
* @notice Pause contract
*/
function pause() external onlyOwner {
_pause();
}
/**
* @notice Unpause contract
*/
function unpause() external onlyOwner {
_unpause();
}
/**
* @notice Emergency withdraw (owner only)
*/
function emergencyWithdraw(address token, uint256 amount) external onlyOwner {
IERC20(token).transfer(owner(), amount);
}
}
interface IERC20 {
function transfer(address to, uint256 amount) external returns (bool);
function approve(address spender, uint256 amount) external returns (bool);
}