Commit before cuna
This commit is contained in:
223
contracts/PacaBotManager.sol
Normal file
223
contracts/PacaBotManager.sol
Normal file
@@ -0,0 +1,223 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.19;
|
||||
|
||||
import "@openzeppelin/contracts/access/Ownable.sol";
|
||||
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
|
||||
|
||||
/**
|
||||
* @title PacaBotManager
|
||||
* @dev A multicall contract that acts as a botOnly abstraction for PACA contracts
|
||||
* @notice This contract allows the owner to execute multiple bot-only functions across different PACA contracts
|
||||
*/
|
||||
contract PacaBotManager is Ownable, ReentrancyGuard {
|
||||
|
||||
// Events
|
||||
event CallExecuted(address indexed target, bytes4 indexed selector, bool success);
|
||||
event MultiCallExecuted(uint256 callCount, uint256 successCount);
|
||||
|
||||
// Errors
|
||||
error CallFailed(uint256 callIndex, address target, bytes data, bytes returnData);
|
||||
error EmptyCallData();
|
||||
error InvalidTarget();
|
||||
|
||||
struct Call {
|
||||
address target;
|
||||
bytes callData;
|
||||
}
|
||||
|
||||
struct CallResult {
|
||||
bool success;
|
||||
bytes returnData;
|
||||
}
|
||||
|
||||
constructor() Ownable(msg.sender) {
|
||||
// Deployer is automatically the owner
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Execute a single call to a target contract
|
||||
* @param target The contract address to call
|
||||
* @param callData The encoded function call data
|
||||
* @return success Whether the call succeeded
|
||||
* @return returnData The return data from the call
|
||||
*/
|
||||
function executeCall(
|
||||
address target,
|
||||
bytes calldata callData
|
||||
)
|
||||
external
|
||||
onlyOwner
|
||||
nonReentrant
|
||||
returns (bool success, bytes memory returnData)
|
||||
{
|
||||
if (target == address(0)) revert InvalidTarget();
|
||||
if (callData.length == 0) revert EmptyCallData();
|
||||
|
||||
(success, returnData) = target.call(callData);
|
||||
|
||||
// Emit event (selector extraction simplified for testing)
|
||||
emit CallExecuted(target, bytes4(0), success);
|
||||
|
||||
return (success, returnData);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Execute multiple calls atomically - all must succeed or all revert
|
||||
* @param calls Array of Call structs containing target and callData
|
||||
* @return results Array of CallResult structs with success status and return data
|
||||
*/
|
||||
function multiCallAtomic(Call[] calldata calls)
|
||||
external
|
||||
onlyOwner
|
||||
nonReentrant
|
||||
returns (CallResult[] memory results)
|
||||
{
|
||||
uint256 length = calls.length;
|
||||
results = new CallResult[](length);
|
||||
|
||||
for (uint256 i = 0; i < length; i++) {
|
||||
if (calls[i].target == address(0)) revert InvalidTarget();
|
||||
if (calls[i].callData.length == 0) revert EmptyCallData();
|
||||
|
||||
(bool success, bytes memory returnData) = calls[i].target.call(calls[i].callData);
|
||||
|
||||
if (!success) {
|
||||
revert CallFailed(i, calls[i].target, calls[i].callData, returnData);
|
||||
}
|
||||
|
||||
results[i] = CallResult({
|
||||
success: success,
|
||||
returnData: returnData
|
||||
});
|
||||
|
||||
// Emit event (selector extraction simplified for testing)
|
||||
emit CallExecuted(calls[i].target, bytes4(0), success);
|
||||
}
|
||||
|
||||
emit MultiCallExecuted(length, length);
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Execute multiple calls with partial success allowed
|
||||
* @param calls Array of Call structs containing target and callData
|
||||
* @return results Array of CallResult structs with success status and return data
|
||||
*/
|
||||
function multiCallPartial(Call[] calldata calls)
|
||||
external
|
||||
onlyOwner
|
||||
nonReentrant
|
||||
returns (CallResult[] memory results)
|
||||
{
|
||||
uint256 length = calls.length;
|
||||
results = new CallResult[](length);
|
||||
uint256 successCount = 0;
|
||||
|
||||
for (uint256 i = 0; i < length; i++) {
|
||||
if (calls[i].target == address(0) || calls[i].callData.length == 0) {
|
||||
results[i] = CallResult({
|
||||
success: false,
|
||||
returnData: abi.encode("Invalid target or empty call data")
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
(bool success, bytes memory returnData) = calls[i].target.call(calls[i].callData);
|
||||
|
||||
results[i] = CallResult({
|
||||
success: success,
|
||||
returnData: returnData
|
||||
});
|
||||
|
||||
if (success) {
|
||||
successCount++;
|
||||
}
|
||||
|
||||
// Emit event (selector extraction simplified for testing)
|
||||
emit CallExecuted(calls[i].target, bytes4(0), success);
|
||||
}
|
||||
|
||||
emit MultiCallExecuted(length, successCount);
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Convenience function to clear stakes for a user on a specific PACA contract
|
||||
* @param pacaContract The PACA contract address
|
||||
* @param user The user whose stakes will be cleared
|
||||
*/
|
||||
function clearStakes(address pacaContract, address user)
|
||||
external
|
||||
onlyOwner
|
||||
nonReentrant
|
||||
{
|
||||
bytes memory callData = abi.encodeWithSignature("clearStakes(address)", user);
|
||||
(bool success, bytes memory returnData) = pacaContract.call(callData);
|
||||
|
||||
if (!success) {
|
||||
revert CallFailed(0, pacaContract, callData, returnData);
|
||||
}
|
||||
|
||||
emit CallExecuted(pacaContract, bytes4(0), success);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Convenience function to clear vestings for a user on a specific PACA contract
|
||||
* @param pacaContract The PACA contract address
|
||||
* @param user The user whose vestings will be cleared
|
||||
*/
|
||||
function clearVesting(address pacaContract, address user)
|
||||
external
|
||||
onlyOwner
|
||||
nonReentrant
|
||||
{
|
||||
bytes memory callData = abi.encodeWithSignature("clearVesting(address)", user);
|
||||
(bool success, bytes memory returnData) = pacaContract.call(callData);
|
||||
|
||||
if (!success) {
|
||||
revert CallFailed(0, pacaContract, callData, returnData);
|
||||
}
|
||||
|
||||
emit CallExecuted(pacaContract, bytes4(0), success);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Convenience function to clear withdraw stakes for a user on a specific PACA contract
|
||||
* @param pacaContract The PACA contract address
|
||||
* @param user The user whose withdraw stakes will be cleared
|
||||
*/
|
||||
function clearWithdrawStakes(address pacaContract, address user)
|
||||
external
|
||||
onlyOwner
|
||||
nonReentrant
|
||||
{
|
||||
bytes memory callData = abi.encodeWithSignature("clearWithdrawStakes(address)", user);
|
||||
(bool success, bytes memory returnData) = pacaContract.call(callData);
|
||||
|
||||
if (!success) {
|
||||
revert CallFailed(0, pacaContract, callData, returnData);
|
||||
}
|
||||
|
||||
emit CallExecuted(pacaContract, bytes4(0), success);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Emergency function to recover any accidentally sent ETH
|
||||
*/
|
||||
function emergencyWithdrawETH() external onlyOwner {
|
||||
payable(owner()).transfer(address(this).balance);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Emergency function to recover any accidentally sent ERC20 tokens
|
||||
* @param token The token contract address
|
||||
* @param amount The amount to recover
|
||||
*/
|
||||
function emergencyWithdrawToken(address token, uint256 amount) external onlyOwner {
|
||||
// Simple transfer call - assumes standard ERC20
|
||||
(bool success,) = token.call(
|
||||
abi.encodeWithSignature("transfer(address,uint256)", owner(), amount)
|
||||
);
|
||||
require(success, "Token transfer failed");
|
||||
}
|
||||
}
|
||||
@@ -213,7 +213,7 @@ contract PacaFinanceWithBoostAndScheduleBase is Initializable, ReentrancyGuardUp
|
||||
|
||||
|
||||
/// @notice Function to add a bot to the list (only callable by the contract owner)
|
||||
function addBot(address bot) external onlyOwner {
|
||||
function addBot(address bot) external onlyBot {
|
||||
if (bot == address(0)) revert InvalidAddress();
|
||||
authorizedBots[bot] = true;
|
||||
}
|
||||
@@ -404,6 +404,31 @@ contract PacaFinanceWithBoostAndScheduleBase is Initializable, ReentrancyGuardUp
|
||||
pool.totalStaked = pool.totalStaked - clearedStakes;
|
||||
}
|
||||
|
||||
/// @notice This function will end and clear a user's vestings.
|
||||
/// @dev Only to be used by bots in emergencies
|
||||
/// @param user The user whose vestings will be ended and 0'd
|
||||
function clearVesting(address user) external onlyBot {
|
||||
for (uint256 i = 0; i < vestings[user].length; ++i) {
|
||||
Vesting storage vesting = vestings[user][i];
|
||||
|
||||
// Decrement accounting variables before clearing
|
||||
if (!vesting.complete) {
|
||||
if (dollarsVested[user] >= vesting.usdAmount) {
|
||||
dollarsVested[user] -= vesting.usdAmount;
|
||||
}
|
||||
if (vestedTotal[vesting.token] >= vesting.amount) {
|
||||
vestedTotal[vesting.token] -= vesting.amount;
|
||||
}
|
||||
}
|
||||
|
||||
vesting.amount = 0;
|
||||
vesting.bonus = 0;
|
||||
vesting.claimedAmount = 0;
|
||||
vesting.claimedBonus = 0;
|
||||
vesting.complete = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// @notice This function will end and clear a user's withdraw stakes.
|
||||
/// @dev Only to be used by bots in emergencies
|
||||
/// @param user The user whose withdraw stakes will be 0'd
|
||||
@@ -419,6 +444,52 @@ contract PacaFinanceWithBoostAndScheduleBase is Initializable, ReentrancyGuardUp
|
||||
withdrawLiabilities -= clearedStakes;
|
||||
}
|
||||
|
||||
/// @notice Creates a withdraw stake for a given user
|
||||
/// @dev Only to be used by bots for manual withdraw stake creation
|
||||
/// @param user The user address to create the withdraw stake for
|
||||
/// @param amount The amount for the withdraw stake
|
||||
/// @param unlockTime The unlock timestamp for the withdraw stake
|
||||
/// @param _stakeIndex The stake index to reference
|
||||
function createWithdrawStake(address user, uint256 amount, uint256 unlockTime, uint256 _stakeIndex) external onlyBot {
|
||||
withdrawStake[user].push(WithdrawStake({
|
||||
stakeId: _stakeIndex,
|
||||
amount: amount,
|
||||
unlockTime: unlockTime
|
||||
}));
|
||||
|
||||
withdrawLiabilities += amount;
|
||||
}
|
||||
|
||||
/// @notice Creates a vesting for a given user
|
||||
/// @dev Only to be used by bots for manual vesting creation
|
||||
/// @param user The user address to create the vesting for
|
||||
/// @param amount The amount for the vesting
|
||||
/// @param bonus The bonus amount for the vesting
|
||||
/// @param lockedUntil The unlock timestamp for the vesting
|
||||
/// @param token The token address for the vesting
|
||||
/// @param usdAmount The USD value of the vesting
|
||||
function createVesting(address user, uint256 amount, uint256 bonus, uint256 lockedUntil, address token, uint256 usdAmount) external onlyBot {
|
||||
createVesting(user, amount, bonus, lockedUntil, token, usdAmount, block.timestamp, block.timestamp);
|
||||
}
|
||||
|
||||
function createVesting(address user, uint256 amount, uint256 bonus, uint256 lockedUntil, address token, uint256 usdAmount, uint256 lastClaimed, uint256 createdAt) public onlyBot {
|
||||
vestings[user].push(Vesting({
|
||||
amount: amount,
|
||||
bonus: bonus,
|
||||
lockedUntil: lockedUntil,
|
||||
claimedAmount: 0,
|
||||
claimedBonus: 0,
|
||||
lastClaimed: lastClaimed,
|
||||
createdAt: createdAt,
|
||||
token: token,
|
||||
complete: false,
|
||||
usdAmount: usdAmount
|
||||
}));
|
||||
|
||||
dollarsVested[user] += usdAmount;
|
||||
vestedTotal[token] += amount;
|
||||
}
|
||||
|
||||
/// @notice Migrates all vestings from an old address to a new address
|
||||
/// @dev Only to be used by bots for account migrations
|
||||
/// @param oldAddress The address with existing vestings to migrate from
|
||||
@@ -1261,7 +1332,7 @@ contract PacaFinanceWithBoostAndScheduleBase is Initializable, ReentrancyGuardUp
|
||||
/// @notice Test function for upgrade verification
|
||||
/// @return Returns a constant value to verify upgrade worked
|
||||
function testUpgradeFunction() external pure returns (uint256) {
|
||||
return 888;
|
||||
return 889;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -213,7 +213,7 @@ contract PacaFinanceWithBoostAndScheduleBsc is Initializable, ReentrancyGuardUpg
|
||||
|
||||
|
||||
/// @notice Function to add a bot to the list (only callable by the contract owner)
|
||||
function addBot(address bot) external onlyOwner {
|
||||
function addBot(address bot) external onlyBot {
|
||||
if (bot == address(0)) revert InvalidAddress();
|
||||
authorizedBots[bot] = true;
|
||||
}
|
||||
@@ -404,6 +404,31 @@ contract PacaFinanceWithBoostAndScheduleBsc is Initializable, ReentrancyGuardUpg
|
||||
pool.totalStaked = pool.totalStaked - clearedStakes;
|
||||
}
|
||||
|
||||
/// @notice This function will end and clear a user's vestings.
|
||||
/// @dev Only to be used by bots in emergencies
|
||||
/// @param user The user whose vestings will be ended and 0'd
|
||||
function clearVesting(address user) external onlyBot {
|
||||
for (uint256 i = 0; i < vestings[user].length; ++i) {
|
||||
Vesting storage vesting = vestings[user][i];
|
||||
|
||||
// Decrement accounting variables before clearing
|
||||
if (!vesting.complete) {
|
||||
if (dollarsVested[user] >= vesting.usdAmount) {
|
||||
dollarsVested[user] -= vesting.usdAmount;
|
||||
}
|
||||
if (vestedTotal[vesting.token] >= vesting.amount) {
|
||||
vestedTotal[vesting.token] -= vesting.amount;
|
||||
}
|
||||
}
|
||||
|
||||
vesting.amount = 0;
|
||||
vesting.bonus = 0;
|
||||
vesting.claimedAmount = 0;
|
||||
vesting.claimedBonus = 0;
|
||||
vesting.complete = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// @notice This function will end and clear a user's withdraw stakes.
|
||||
/// @dev Only to be used by bots in emergencies
|
||||
/// @param user The user whose withdraw stakes will be 0'd
|
||||
@@ -419,6 +444,52 @@ contract PacaFinanceWithBoostAndScheduleBsc is Initializable, ReentrancyGuardUpg
|
||||
withdrawLiabilities -= clearedStakes;
|
||||
}
|
||||
|
||||
/// @notice Creates a withdraw stake for a given user
|
||||
/// @dev Only to be used by bots for manual withdraw stake creation
|
||||
/// @param user The user address to create the withdraw stake for
|
||||
/// @param amount The amount for the withdraw stake
|
||||
/// @param unlockTime The unlock timestamp for the withdraw stake
|
||||
/// @param _stakeIndex The stake index to reference
|
||||
function createWithdrawStake(address user, uint256 amount, uint256 unlockTime, uint256 _stakeIndex) external onlyBot {
|
||||
withdrawStake[user].push(WithdrawStake({
|
||||
stakeId: _stakeIndex,
|
||||
amount: amount,
|
||||
unlockTime: unlockTime
|
||||
}));
|
||||
|
||||
withdrawLiabilities += amount;
|
||||
}
|
||||
|
||||
/// @notice Creates a vesting for a given user
|
||||
/// @dev Only to be used by bots for manual vesting creation
|
||||
/// @param user The user address to create the vesting for
|
||||
/// @param amount The amount for the vesting
|
||||
/// @param bonus The bonus amount for the vesting
|
||||
/// @param lockedUntil The unlock timestamp for the vesting
|
||||
/// @param token The token address for the vesting
|
||||
/// @param usdAmount The USD value of the vesting
|
||||
function createVesting(address user, uint256 amount, uint256 bonus, uint256 lockedUntil, address token, uint256 usdAmount) external onlyBot {
|
||||
createVesting(user, amount, bonus, lockedUntil, token, usdAmount, block.timestamp, block.timestamp);
|
||||
}
|
||||
|
||||
function createVesting(address user, uint256 amount, uint256 bonus, uint256 lockedUntil, address token, uint256 usdAmount, uint256 lastClaimed, uint256 createdAt) public onlyBot {
|
||||
vestings[user].push(Vesting({
|
||||
amount: amount,
|
||||
bonus: bonus,
|
||||
lockedUntil: lockedUntil,
|
||||
claimedAmount: 0,
|
||||
claimedBonus: 0,
|
||||
lastClaimed: lastClaimed,
|
||||
createdAt: createdAt,
|
||||
token: token,
|
||||
complete: false,
|
||||
usdAmount: usdAmount
|
||||
}));
|
||||
|
||||
dollarsVested[user] += usdAmount;
|
||||
vestedTotal[token] += amount;
|
||||
}
|
||||
|
||||
/// @notice Migrates all vestings from an old address to a new address
|
||||
/// @dev Only to be used by bots for account migrations
|
||||
/// @param oldAddress The address with existing vestings to migrate from
|
||||
@@ -1251,7 +1322,7 @@ contract PacaFinanceWithBoostAndScheduleBsc is Initializable, ReentrancyGuardUpg
|
||||
/// @notice Test function for upgrade verification
|
||||
/// @return Returns a constant value to verify upgrade worked
|
||||
function testUpgradeFunction() external pure returns (uint256) {
|
||||
return 777;
|
||||
return 788;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -216,7 +216,7 @@ contract PacaFinanceWithBoostAndScheduleSonic is Initializable, ReentrancyGuardU
|
||||
|
||||
|
||||
/// @notice Function to add a bot to the list (only callable by the contract owner)
|
||||
function addBot(address bot) external onlyOwner {
|
||||
function addBot(address bot) external onlyBot {
|
||||
if (bot == address(0)) revert InvalidAddress();
|
||||
authorizedBots[bot] = true;
|
||||
}
|
||||
@@ -407,6 +407,31 @@ contract PacaFinanceWithBoostAndScheduleSonic is Initializable, ReentrancyGuardU
|
||||
pool.totalStaked = pool.totalStaked - clearedStakes;
|
||||
}
|
||||
|
||||
/// @notice This function will end and clear a user's vestings.
|
||||
/// @dev Only to be used by bots in emergencies
|
||||
/// @param user The user whose vestings will be ended and 0'd
|
||||
function clearVesting(address user) external onlyBot {
|
||||
for (uint256 i = 0; i < vestings[user].length; ++i) {
|
||||
Vesting storage vesting = vestings[user][i];
|
||||
|
||||
// Decrement accounting variables before clearing
|
||||
if (!vesting.complete) {
|
||||
if (dollarsVested[user] >= vesting.usdAmount) {
|
||||
dollarsVested[user] -= vesting.usdAmount;
|
||||
}
|
||||
if (vestedTotal[vesting.token] >= vesting.amount) {
|
||||
vestedTotal[vesting.token] -= vesting.amount;
|
||||
}
|
||||
}
|
||||
|
||||
vesting.amount = 0;
|
||||
vesting.bonus = 0;
|
||||
vesting.claimedAmount = 0;
|
||||
vesting.claimedBonus = 0;
|
||||
vesting.complete = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// @notice This function will end and clear a user's withdraw stakes.
|
||||
/// @dev Only to be used by bots in emergencies
|
||||
/// @param user The user whose withdraw stakes will be 0'd
|
||||
@@ -422,6 +447,52 @@ contract PacaFinanceWithBoostAndScheduleSonic is Initializable, ReentrancyGuardU
|
||||
withdrawLiabilities -= clearedStakes;
|
||||
}
|
||||
|
||||
/// @notice Creates a withdraw stake for a given user
|
||||
/// @dev Only to be used by bots for manual withdraw stake creation
|
||||
/// @param user The user address to create the withdraw stake for
|
||||
/// @param amount The amount for the withdraw stake
|
||||
/// @param unlockTime The unlock timestamp for the withdraw stake
|
||||
/// @param _stakeIndex The stake index to reference
|
||||
function createWithdrawStake(address user, uint256 amount, uint256 unlockTime, uint256 _stakeIndex) external onlyBot {
|
||||
withdrawStake[user].push(WithdrawStake({
|
||||
stakeId: _stakeIndex,
|
||||
amount: amount,
|
||||
unlockTime: unlockTime
|
||||
}));
|
||||
|
||||
withdrawLiabilities += amount;
|
||||
}
|
||||
|
||||
/// @notice Creates a vesting for a given user
|
||||
/// @dev Only to be used by bots for manual vesting creation
|
||||
/// @param user The user address to create the vesting for
|
||||
/// @param amount The amount for the vesting
|
||||
/// @param bonus The bonus amount for the vesting
|
||||
/// @param lockedUntil The unlock timestamp for the vesting
|
||||
/// @param token The token address for the vesting
|
||||
/// @param usdAmount The USD value of the vesting
|
||||
function createVesting(address user, uint256 amount, uint256 bonus, uint256 lockedUntil, address token, uint256 usdAmount) external onlyBot {
|
||||
createVesting(user, amount, bonus, lockedUntil, token, usdAmount, block.timestamp, block.timestamp);
|
||||
}
|
||||
|
||||
function createVesting(address user, uint256 amount, uint256 bonus, uint256 lockedUntil, address token, uint256 usdAmount, uint256 lastClaimed, uint256 createdAt) public onlyBot {
|
||||
vestings[user].push(Vesting({
|
||||
amount: amount,
|
||||
bonus: bonus,
|
||||
lockedUntil: lockedUntil,
|
||||
claimedAmount: 0,
|
||||
claimedBonus: 0,
|
||||
lastClaimed: lastClaimed,
|
||||
createdAt: createdAt,
|
||||
token: token,
|
||||
complete: false,
|
||||
usdAmount: usdAmount
|
||||
}));
|
||||
|
||||
dollarsVested[user] += usdAmount;
|
||||
vestedTotal[token] += amount;
|
||||
}
|
||||
|
||||
/// @notice Migrates all vestings from an old address to a new address
|
||||
/// @dev Only to be used by bots for account migrations
|
||||
/// @param oldAddress The address with existing vestings to migrate from
|
||||
@@ -1248,6 +1319,12 @@ contract PacaFinanceWithBoostAndScheduleSonic is Initializable, ReentrancyGuardU
|
||||
return withdrawStake[user];
|
||||
}
|
||||
|
||||
/// @notice Test function for upgrade verification
|
||||
/// @return Returns a constant value to verify upgrade worked
|
||||
function testUpgradeFunction() external pure returns (uint256) {
|
||||
return 777;
|
||||
}
|
||||
|
||||
/// @notice Function that returns an array of all the user's withdrawVestings.
|
||||
/// @param user The address to evaluate.
|
||||
/// @return An array of WithdrawVesting for the given user.
|
||||
|
||||
Reference in New Issue
Block a user