Skip to content

Commit

Permalink
feat: bonus logic
Browse files Browse the repository at this point in the history
  • Loading branch information
ashitakah committed Apr 10, 2024
1 parent de5bdbb commit acd26b6
Show file tree
Hide file tree
Showing 3 changed files with 160 additions and 18 deletions.
49 changes: 40 additions & 9 deletions solidity/contracts/periphery/Keep3rSponsor.sol
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.19;

import {IKeep3rSponsor, IKeep3rV2} from '../../interfaces/periphery/IKeep3rSponsor.sol';
import {IAutomationVault, IOpenRelay} from '../../interfaces/relays/IOpenRelay.sol';
import {EnumerableSet} from 'openzeppelin/utils/structs/EnumerableSet.sol';

import {IKeep3rSponsor, IKeep3rV2, IKeep3rHelper} from '../../interfaces/periphery/IKeep3rSponsor.sol';
import {IAutomationVault, IOpenRelay} from '../../interfaces/relays/IOpenRelay.sol';

/**
* @title Keep3rSponsor
* @notice This contract managed by Keep3r Network will sponsor some execution in determined jobs
Expand All @@ -16,6 +17,15 @@ contract Keep3rSponsor is IKeep3rSponsor {
/// @inheritdoc IKeep3rSponsor
IKeep3rV2 public immutable KEEP3R_V2;

/// @inheritdoc IKeep3rSponsor
IKeep3rHelper public immutable KEEP3R_HELPER;

/// @inheritdoc IKeep3rSponsor
uint32 public immutable BASE = 10_000;

/// @inheritdoc IKeep3rSponsor
uint256 public bonus = 15_000;

/// @inheritdoc IKeep3rSponsor
IOpenRelay public openRelay;

Expand All @@ -37,12 +47,21 @@ contract Keep3rSponsor is IKeep3rSponsor {
* @param _owner The address of the owner
* @param _feeRecipient The address of the fee recipient
* @param _openRelay The address of the open relay
* @param _keep3rV2 The address of the keep3rV2
* @param _keep3rHelper The address of the keep3rHelper
*/
constructor(address _owner, address _feeRecipient, IOpenRelay _openRelay, IKeep3rV2 _keep3rV2) {
openRelay = _openRelay;
constructor(
address _owner,
address _feeRecipient,
IOpenRelay _openRelay,
IKeep3rV2 _keep3rV2,
IKeep3rHelper _keep3rHelper
) {
owner = _owner;
feeRecipient = _feeRecipient;
openRelay = _openRelay;
KEEP3R_V2 = _keep3rV2;
KEEP3R_HELPER = _keep3rHelper;
}

/// @inheritdoc IKeep3rSponsor
Expand Down Expand Up @@ -75,6 +94,12 @@ contract Keep3rSponsor is IKeep3rSponsor {
emit OpenRelaySetted(_openRelay);
}

/// @inheritdoc IKeep3rSponsor
function setBonus(uint256 _bonus) external onlyOwner {
bonus = _bonus;
emit BonusSetted(_bonus);
}

/// @inheritdoc IKeep3rSponsor
function addSponsoredJobs(address[] calldata _jobs) public onlyOwner {
for (uint256 _i; _i < _jobs.length;) {
Expand All @@ -100,6 +125,13 @@ contract Keep3rSponsor is IKeep3rSponsor {
}

function exec(IAutomationVault _automationVault, IAutomationVault.ExecData[] calldata _execData) external {
if (_execData.length == 0) revert Keep3rSponsor_NoJobs();

// The first call to `isKeeper` ensures the caller is a valid keeper
bool _isKeeper = KEEP3R_V2.isKeeper(msg.sender);
if (!_isKeeper) revert Keep3rSponsor_NotKeeper();
uint256 _initialGas = gasleft();

for (uint256 _i; _i < _execData.length;) {
if (!_sponsoredJobs.contains(_execData[_i].job)) revert Keep3rSponsor_JobNotSponsored();

Expand All @@ -108,13 +140,12 @@ contract Keep3rSponsor is IKeep3rSponsor {
}
}

// The first call to `isKeeper` ensures the caller is a valid keeper
bool _isKeeper = KEEP3R_V2.isKeeper(msg.sender);
if (!_isKeeper) revert Keep3rSponsor_NotKeeper();

openRelay.exec(_automationVault, _execData, feeRecipient);

KEEP3R_V2.worked(msg.sender);
uint256 _gasAfterWork = gasleft();
uint256 _reward = IKeep3rHelper(KEEP3R_HELPER).getRewardAmountFor(msg.sender, _initialGas - _gasAfterWork);
_reward = (_reward * bonus) / BASE;
KEEP3R_V2.bondedPayment(msg.sender, _reward);
}

/**
Expand Down
37 changes: 37 additions & 0 deletions solidity/interfaces/periphery/IKeep3rSponsor.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ pragma solidity 0.8.19;

import {IAutomationVault, IOpenRelay} from '../../interfaces/relays/IOpenRelay.sol';
import {IKeep3rV2} from '../../interfaces/external/IKeep3rV2.sol';
import {IKeep3rHelper} from '../../interfaces/external/IKeep3rHelper.sol';

interface IKeep3rSponsor {
/*///////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -39,6 +40,12 @@ interface IKeep3rSponsor {
*/
event OpenRelaySetted(IOpenRelay indexed _openRelay);

/**
* @notice Emitted when the bonus is setted
* @param _bonus The sponsored bonus
*/
event BonusSetted(uint256 indexed _bonus);

/**
* @notice Emitted when a sponsored job is approved
* @param _job The address of the sponsored job
Expand Down Expand Up @@ -75,6 +82,11 @@ interface IKeep3rSponsor {
*/
error Keep3rSponsor_NotKeeper();

/**
* @notice Thrown when the exec data is empty
*/
error Keep3rSponsor_NoJobs();

/*///////////////////////////////////////////////////////////////
VIEW FUNCTIONS
//////////////////////////////////////////////////////////////*/
Expand All @@ -85,6 +97,25 @@ interface IKeep3rSponsor {
*/
function KEEP3R_V2() external view returns (IKeep3rV2 _keep3rV2);

/**
* @notice Returns the keep3r helper contract
* @return _keep3rHelper The address of the keep3r helper contract
*/
function KEEP3R_HELPER() external view returns (IKeep3rHelper _keep3rHelper);

/**
* @notice Returns the base
* @return _base The base
*/
function BASE() external view returns (uint32 _base);

/**
* @notice Returns the bonus
* @dev The bonus is in base 10_000
* @return _bonus The bonus
*/
function bonus() external view returns (uint256 _bonus);

/**
* @notice Returns the open relay
* @return _openRelay The address of the open relay
Expand Down Expand Up @@ -143,6 +174,12 @@ interface IKeep3rSponsor {
*/
function setOpenRelay(IOpenRelay _openRelay) external;

/**
* @notice Sets the bonus
* @param _bonus The bonus
*/
function setBonus(uint256 _bonus) external;

/**
* @notice Adds a job to the sponsored list
* @param _jobs List of jobs to add
Expand Down
92 changes: 83 additions & 9 deletions solidity/test/unit/Keep3rSponsor.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ import {
IKeep3rSponsor,
IOpenRelay,
IAutomationVault,
IKeep3rV2,
IKeep3rHelper,
EnumerableSet
} from '../../contracts/periphery/Keep3rSponsor.sol';
import {IKeep3rV2} from '../../interfaces/external/IKeep3rV2.sol';

contract Keep3rSponsorForTest is Keep3rSponsor {
using EnumerableSet for EnumerableSet.AddressSet;
Expand All @@ -19,8 +20,9 @@ contract Keep3rSponsorForTest is Keep3rSponsor {
address _owner,
address _feeRecipient,
IOpenRelay _openRelay,
IKeep3rV2 _keep3rV2
) Keep3rSponsor(_owner, _feeRecipient, _openRelay, _keep3rV2) {}
IKeep3rV2 _keep3rV2,
IKeep3rHelper _keep3rHelper
) Keep3rSponsor(_owner, _feeRecipient, _openRelay, _keep3rV2, _keep3rHelper) {}

function addSponsorJobsForTest(address[] memory _jobs) external {
for (uint256 _i; _i < _jobs.length;) {
Expand Down Expand Up @@ -49,13 +51,15 @@ contract Keep3rSponsorUnitTest is Test {
event OpenRelaySetted(IOpenRelay indexed _openRelay);
event ApproveSponsoredJob(address indexed _job);
event DeleteSponsoredJob(address indexed _job);
event BonusSetted(uint256 indexed _bonus);

// Keep3rSponsor contract
Keep3rSponsorForTest public keep3rSponsor;

// External contracts
IOpenRelay public openRelay;
IKeep3rV2 public keep3rV2;
IKeep3rHelper public keep3rHelper;

/// EOAs
address public owner;
Expand All @@ -69,8 +73,9 @@ contract Keep3rSponsorUnitTest is Test {

openRelay = IOpenRelay(makeAddr('OpenRelay'));
keep3rV2 = IKeep3rV2(makeAddr('Keep3rV2'));
keep3rHelper = IKeep3rHelper(makeAddr('Keep3rHelper'));

keep3rSponsor = new Keep3rSponsorForTest(owner, feeRecipient, openRelay, keep3rV2);
keep3rSponsor = new Keep3rSponsorForTest(owner, feeRecipient, openRelay, keep3rV2, keep3rHelper);
}

/**
Expand All @@ -88,6 +93,7 @@ contract UnitKeep3rSponsorConstructor is Keep3rSponsorUnitTest {
assertEq(keep3rSponsor.feeRecipient(), feeRecipient);
assertEq(address(keep3rSponsor.openRelay()), address(openRelay));
assertEq(address(keep3rSponsor.KEEP3R_V2()), address(keep3rV2));
assertEq(address(keep3rSponsor.KEEP3R_HELPER()), address(keep3rHelper));
}
}

Expand Down Expand Up @@ -233,6 +239,27 @@ contract UnitKeep3rSponsorSetOpenRelay is Keep3rSponsorUnitTest {
}
}

contract UnitKeep3rSponsorSetBonus is Keep3rSponsorUnitTest {
function setUp() public override {
Keep3rSponsorUnitTest.setUp();

vm.startPrank(owner);
}

function testSetBonus(uint256 _bonus) public {
keep3rSponsor.setBonus(_bonus);

assertEq(keep3rSponsor.bonus(), _bonus);
}

function testEmitBonusSetted(uint256 _bonus) public {
vm.expectEmit();
emit BonusSetted(_bonus);

keep3rSponsor.setBonus(_bonus);
}
}

contract UnitKeep3rSponsorAddSponsoredJobs is Keep3rSponsorUnitTest {
using EnumerableSet for EnumerableSet.AddressSet;

Expand Down Expand Up @@ -355,18 +382,28 @@ contract UnitKeep3rSponsorExec is Keep3rSponsorUnitTest {
abi.encodeWithSelector(IOpenRelay.exec.selector, _automationVault, _execData, feeRecipient),
abi.encode(true)
);
vm.mockCall(address(keep3rV2), abi.encodeWithSelector(IKeep3rV2.worked.selector, address(this)), abi.encode(true));

_;
}

function testRevertIfNoJobs(
IAutomationVault _automationVault,
IAutomationVault.ExecData[] memory _execData
) public happyPath(_automationVault, _execData) {
vm.expectRevert(abi.encodeWithSelector(IKeep3rSponsor.Keep3rSponsor_NoJobs.selector));

keep3rSponsor.exec(_automationVault, new IAutomationVault.ExecData[](0));
}

function testRevertIfJobIsNotSponsored(
IAutomationVault _automationVault,
IAutomationVault.ExecData[] memory _execData
) public {
vm.assume(_execData.length > 0 && _execData.length < 5);
vm.assume(_automationVault != IAutomationVault(address(0)));

vm.expectRevert(abi.encodeWithSelector(IKeep3rSponsor.Keep3rSponsor_JobNotSponsored.selector));
vm.mockCall(address(keep3rV2), abi.encodeWithSelector(IKeep3rV2.isKeeper.selector, address(this)), abi.encode(true));

_execData[0].job = makeAddr('JobNotSponsored');

keep3rSponsor.exec(_automationVault, _execData);
}
Expand All @@ -386,12 +423,49 @@ contract UnitKeep3rSponsorExec is Keep3rSponsorUnitTest {

function testExecOpenRelay(
IAutomationVault _automationVault,
IAutomationVault.ExecData[] memory _execData
IAutomationVault.ExecData[] memory _execData,
uint128 _reward
) public happyPath(_automationVault, _execData) {
vm.assume(_reward > 0);
vm.expectCall(
address(openRelay), abi.encodeWithSelector(IOpenRelay.exec.selector, _automationVault, _execData, feeRecipient)
);
vm.expectCall(address(keep3rV2), abi.encodeWithSelector(IKeep3rV2.worked.selector, address(this)));

vm.mockCall(
address(keep3rHelper), abi.encodeWithSelector(IKeep3rHelper.getRewardAmountFor.selector), abi.encode(_reward)
);

vm.mockCall(
address(keep3rV2),
abi.encodeWithSelector(IKeep3rV2.bondedPayment.selector, address(this), _reward),
abi.encode(true)
);

keep3rSponsor.exec(_automationVault, _execData);
}

function testExecRewardAmount(
IAutomationVault _automationVault,
IAutomationVault.ExecData[] memory _execData,
uint128 _reward
) public happyPath(_automationVault, _execData) {
vm.assume(_reward > 0);

vm.mockCall(
address(keep3rHelper), abi.encodeWithSelector(IKeep3rHelper.getRewardAmountFor.selector), abi.encode(_reward)
);

uint256 rewardWithBonus = (_reward * keep3rSponsor.bonus()) / keep3rSponsor.BASE();

Check warning on line 458 in solidity/test/unit/Keep3rSponsor.t.sol

View workflow job for this annotation

GitHub Actions / Run Linters (16.x)

'rewardWithBonus' should start with _

vm.mockCall(
address(keep3rV2),
abi.encodeWithSelector(IKeep3rV2.bondedPayment.selector, address(this), rewardWithBonus),
abi.encode(true)
);

vm.expectCall(
address(keep3rV2), abi.encodeWithSelector(IKeep3rV2.bondedPayment.selector, address(this), rewardWithBonus)
);

keep3rSponsor.exec(_automationVault, _execData);
}
Expand Down

0 comments on commit acd26b6

Please sign in to comment.