Skip to content

Commit

Permalink
Merge pull request #262 from eonian-core/development
Browse files Browse the repository at this point in the history
migrete staging from gelato automation to gelato functions
  • Loading branch information
LeoVS09 authored Jun 19, 2024
2 parents 790fa1c + f3d846c commit 2321f20
Show file tree
Hide file tree
Showing 24 changed files with 995 additions and 101 deletions.
401 changes: 401 additions & 0 deletions packages/contracts/.openzeppelin/bsc.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion packages/contracts/.solhint.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"extends": "solhint:recommended",
"rules": {
"compiler-version": ["error", "^0.8.19"],
"compiler-version": ["error", "^0.8.26"],
"func-visibility": ["error", { "ignoreConstructors": true }],
"code-complexity": ["error", 9],
"function-max-lines": ["error", 50],
Expand Down
17 changes: 11 additions & 6 deletions packages/contracts/src/automation/GelatoJobAdapter.sol
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.26;

import {IJob} from "./IJob.sol";
import {Job} from "./Job.sol";
import {IResolver} from "./gelato/IResolver.sol";
import {OpsReady, IOps} from "./gelato/OpsReady.sol";
import {IChecker} from "./gelato/IChecker.sol";
import {IPayableJob} from "./IPayableJob.sol";

/// @notice Contract expect work will be prepayd, so it cannot pay for work
error PayableWorkNotAllowed();

/// @title Implementation of the mixin that adds support for Gelato (keepers operator)
abstract contract GelatoJobAdapter is Job, IResolver, OpsReady {
abstract contract GelatoJobAdapter is Job, IChecker, OpsReady, IPayableJob {
/// @notice If job is prepaid, then it not will try to pay on executed work.
bool public isPrepaid;

Expand Down Expand Up @@ -56,11 +58,14 @@ abstract contract GelatoJobAdapter is Job, IResolver, OpsReady {
view
returns (bool canExec, bytes memory execPayload)
{
canExec = canWork();
(canExec, execPayload) = canWork();
if(!canExec) {
return (canExec, execPayload);
}

execPayload = abi.encodeWithSelector(
isPrepaid ? this.work.selector : this.payableWork.selector
);
execPayload = isPrepaid
? abi.encodeCall(IJob.work, ())
: abi.encodeCall(IPayableJob.payableWork, ());
}

/// @notice Bot will call this method when `checker` returns `true`.
Expand Down
7 changes: 7 additions & 0 deletions packages/contracts/src/automation/IJob.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.26;

interface IJob {
function canWork() external view returns (bool canExec, bytes memory reason);
function work() external;
}
8 changes: 8 additions & 0 deletions packages/contracts/src/automation/IPayableJob.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.26;

import {IJob} from "./IJob.sol";

interface IPayableJob is IJob {
function payableWork() external;
}
24 changes: 17 additions & 7 deletions packages/contracts/src/automation/Job.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {ReentrancyGuardUpgradeable} from "@openzeppelin/contracts-upgradeable/se
import {ContextUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol";

import {SafeInitializable} from "../upgradeable/SafeInitializable.sol";
import {IJob} from "./IJob.sol";

/// Someone tried to execute work function while `canWork` is `false`
error CannotWorkNow();
Expand All @@ -19,7 +20,8 @@ error TimeMinimumBetweenExecutionsIncorrect(uint256 _givenTime);
abstract contract Job is
SafeInitializable,
ContextUpgradeable,
ReentrancyGuardUpgradeable
ReentrancyGuardUpgradeable,
IJob
{
/// Job work function was executed by worker bot
event Worked(address indexed worker);
Expand Down Expand Up @@ -77,15 +79,22 @@ abstract contract Job is

/// @notice If work can be executed by keeper at this moment returns true
/// @dev Will be executed by keeper and before `work` method execution.
function canWork() public view returns (bool) {
function canWork() public view returns (bool canExec, bytes memory reason) {
// TODO: Check the maximum delay between job executions (?)
return
isTimePassFromLastExecution(minimumBetweenExecutions) && _canWork();
if(isTimePassFromLastExecution(minimumBetweenExecutions) == false) {
return (false, bytes("Minimum time between executions not passed"));
}

// TODO: possible to add check for high gas price
// https://docs.gelato.network/web3-services/web3-functions/quick-start/writing-solidity-functions#id-6.-limit-the-gas-price-of-your-execution

return _canWork();
}

/// @notice allow execution only if `canWork` return true
modifier onlyWhenCanWork() {
if (!canWork()) {
(bool canExec, ) = canWork();
if (!canExec) {
revert CannotWorkNow();
}
_;
Expand Down Expand Up @@ -153,6 +162,7 @@ abstract contract Job is

/// @notice Method which identify if work can be executed at this moment.
/// @dev Will be executed by keeper and before `work` method execution.
/// @return true if `work` method can be called.
function _canWork() internal view virtual returns (bool);
/// @return canExec - true if `work` method can be called.
/// @return reason - if `canExec` is false, then reason why work can't be executed.
function _canWork() internal view virtual returns (bool canExec, bytes memory reason);
}
4 changes: 2 additions & 2 deletions packages/contracts/src/automation/example/SimpleGelatoJob.sol
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ contract SimpleGelatoJob is GelatoJobAdapter, SafeUUPSUpgradeable {
workMethodCalledCounter++;
}

function _canWork() internal view override returns (bool) {
return canWorkResult;
function _canWork() internal view override returns (bool canExec, bytes memory reason) {
return (canWorkResult, bytes(""));
}

function setCanWorkResult(bool _canWorkResult) public onlyOwner {
Expand Down
14 changes: 14 additions & 0 deletions packages/contracts/src/automation/gelato/IChecker.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.26;

/// Migraed from Gelato Automation IResolver interface
/// @dev Based on https://github.com/gelatodigital/ops/blob/9a9cde6ab2f1b132b949f9244fd59a1de4da4123/contracts/interfaces/IResolver.sol
interface IChecker {

/// @param canExec (Boolean): Indicates if Gelato should execute the task
/// @param execPayload (Bytes): Contains the data that executors will use during execution.
function checker()
external
view
returns (bool canExec, bytes memory execPayload);
}
148 changes: 145 additions & 3 deletions packages/contracts/src/automation/gelato/IOps.sol
Original file line number Diff line number Diff line change
@@ -1,9 +1,151 @@
// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.26;

/// @dev Based on https://github.com/gelatodigital/ops
import { ITaskTreasuryUpgradable } from "./ITaskTreasuryUpgradable.sol";

enum Module {
RESOLVER,
TIME,
PROXY,
SINGLE_EXEC
}

struct ModuleData {
Module[] modules;
bytes[] args;
}

/// In gelato docs and codebase named as IAutomate,
/// but it fully match with legacy IOps that we used in our contracts
/// To avoid unnecesary changes we will use IOps name
///
/// Based on
/// - https://github.com/gelatodigital/automate-unit-testing/blob/main/contracts/gelato/Types.sol
/// - https://github.com/gelatodigital/automate-unit-testing/blob/main/contracts/gelato/IAutomate.sol
/// - https://github.com/gelatodigital/ops
interface IOps {
function gelato() external view returns (address payable);
// solhint-disable max-line-length
/**
* @notice Initiates a task with conditions which Gelato will monitor and execute when conditions are met.
*
* @param execAddress Address of contract that should be called by Gelato.
* @param execData Execution data to be called with / function selector if execution data is yet to be determined.
* @param moduleData Conditional modules that will be used. {See LibDataTypes-ModuleData}
* @param feeToken Address of token to be used as payment. Use address(0) if TaskTreasury is being used, 0xeeeeee... for ETH or native tokens.
*
* @return taskId Unique hash of the task created.
*/
function createTask(
address execAddress,
bytes calldata execData,
ModuleData calldata moduleData,
address feeToken
) external returns (bytes32 taskId);

/**
* @notice Terminates a task that was created and Gelato can no longer execute it.
*
* @param taskId Unique hash of the task that is being cancelled. {See LibTaskId-getTaskId}
*/
function cancelTask(bytes32 taskId) external;

/**
* @notice Execution API called by Gelato.
*
* @param taskCreator The address which created the task.
* @param execAddress Address of contract that should be called by Gelato.
* @param execData Execution data to be called with / function selector if execution data is yet to be determined.
* @param moduleData Conditional modules that will be used. {See LibDataTypes-ModuleData}
* @param txFee Fee paid to Gelato for execution, deducted on the TaskTreasury or transfered to Gelato.
* @param feeToken Token used to pay for the execution. ETH = 0xeeeeee...
* @param useTaskTreasuryFunds If taskCreator's balance on TaskTreasury should pay for the tx.
* @param revertOnFailure To revert or not if call to execAddress fails. (Used for off-chain simulations)
*/
function exec(
address taskCreator,
address execAddress,
bytes memory execData,
ModuleData calldata moduleData,
uint256 txFee,
address feeToken,
bool useTaskTreasuryFunds,
bool revertOnFailure
) external;

/**
* @notice Sets the address of task modules. Only callable by proxy admin.
*
* @param modules List of modules to be set
* @param moduleAddresses List of addresses for respective modules.
*/
function setModule(
Module[] calldata modules,
address[] calldata moduleAddresses
) external;

/**
* @notice Helper function to query fee and feeToken to be used for payment. (For executions which pays itself)
*
* @return uint256 Fee amount to be paid.
* @return address Token to be paid. (Determined and passed by taskCreator during createTask)
*/
function getFeeDetails() external view returns (uint256, address);

/**
* @notice Helper func to query all open tasks by a task creator.
*
* @param taskCreator Address of task creator to query.
*
* @return bytes32[] List of taskIds created.
*/
function getTaskIdsByUser(address taskCreator)
external
view
returns (bytes32[] memory);

/**
* @notice TaskTreasury contract where user deposit funds to be used for fee payments.
*
* @return ITaskTreasuryUpgradable TaskTreasury contract interface
*/
function taskTreasury() external view returns (ITaskTreasuryUpgradable);

/**
* @notice Helper function to compute task id with module arguments
*
* @param taskCreator The address which created the task.
* @param execAddress Address of contract that will be called by Gelato.
* @param execSelector Signature of the function which will be called by Gelato.
* @param moduleData Conditional modules that will be used. {See LibDataTypes-ModuleData}
* @param feeToken Address of token to be used as payment. Use address(0) if TaskTreasury is being used, 0xeeeeee... for ETH or native tokens.
*/
function getTaskId(
address taskCreator,
address execAddress,
bytes4 execSelector,
ModuleData memory moduleData,
address feeToken
) external pure returns (bytes32 taskId);

/**
* @notice (Legacy) Helper function to compute task id.
*
* @param taskCreator The address which created the task.
* @param execAddress Address of contract that will be called by Gelato.
* @param execSelector Signature of the function which will be called by Gelato.
* @param useTaskTreasuryFunds Wether fee should be deducted from TaskTreasury.
* @param feeToken Address of token to be used as payment. Use address(0) if TaskTreasury is being used, 0xeeeeee... for ETH or native tokens.
* @param resolverHash Hash of resolverAddress and resolverData {See getResolverHash}
*/
function getTaskId(
address taskCreator,
address execAddress,
bytes4 execSelector,
bool useTaskTreasuryFunds,
address feeToken,
bytes32 resolverHash
) external pure returns (bytes32);

function gelato() external view returns (address payable);

function getFeeDetails() external view returns (uint256, address);
}
56 changes: 56 additions & 0 deletions packages/contracts/src/automation/gelato/IOpsProxy.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.26;

/// Based on https://github.com/gelatodigital/automate-unit-testing/blob/main/contracts/gelato/IOpsProxy.sol
interface IOpsProxy {
/**
* @notice Emitted when proxy calls a contract successfully in `executeCall`
*
* @param target Address of contract that is called
* @param data Data used in the call.
* @param value Native token value used in the call.
* @param returnData Data returned by the call.
*/
event ExecuteCall(
address indexed target, bytes data, uint256 value, bytes returnData
);

/**
* @notice Multicall to different contracts with different datas.
*
* @param targets Addresses of contracts to be called.
* @param datas Datas for each contract call.
* @param values Native token value for each contract call.
*/
function batchExecuteCall(
address[] calldata targets,
bytes[] calldata datas,
uint256[] calldata values
) external payable;

/**
* @notice Call to a single contract.
*
* @param target Address of contracts to be called.
* @param data Data for contract call.
* @param value Native token value for contract call.
*/
function executeCall(address target, bytes calldata data, uint256 value)
external
payable;

/**
* @return address Ops smart contract address
*/
function ops() external view returns (address);

/**
* @return address Owner of the proxy
*/
function owner() external view returns (address);

/**
* @return uint256 version of OpsProxy.
*/
function version() external view returns (uint256);
}
9 changes: 9 additions & 0 deletions packages/contracts/src/automation/gelato/IOpsProxyFactory.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.26;

/// Based on https://github.com/gelatodigital/automate-unit-testing/blob/main/contracts/gelato/Types.sol
interface IOpsProxyFactory {
function deployFor(address owner) external returns (address payable proxy);
function getProxyOf(address account) external view returns (address, bool);
function deploy() external returns (address payable proxy);
}
10 changes: 0 additions & 10 deletions packages/contracts/src/automation/gelato/IResolver.sol

This file was deleted.

Loading

0 comments on commit 2321f20

Please sign in to comment.