Skip to content

Commit

Permalink
feat: last comments and native token
Browse files Browse the repository at this point in the history
  • Loading branch information
ashitakah committed Dec 26, 2023
1 parent 51e1475 commit 7159d89
Show file tree
Hide file tree
Showing 8 changed files with 113 additions and 63 deletions.
42 changes: 22 additions & 20 deletions solidity/contracts/AutomationVault.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ pragma solidity 0.8.19;
import {IAutomationVault} from '@interfaces/IAutomationVault.sol';
import {IERC20, SafeERC20} from '@openzeppelin/token/ERC20/utils/SafeERC20.sol';
import {EnumerableSet} from '@openzeppelin/utils/structs/EnumerableSet.sol';
import {_ETH, _ALL} from '@utils/Constants.sol';
import {_ALL} from '@utils/Constants.sol';

/**
* @title AutomationVault
Expand All @@ -19,7 +19,8 @@ contract AutomationVault is IAutomationVault {
address public owner;
/// @inheritdoc IAutomationVault
address public pendingOwner;

/// @inheritdoc IAutomationVault
address public immutable nativeToken;

Check warning on line 23 in solidity/contracts/AutomationVault.sol

View workflow job for this annotation

GitHub Actions / Run Linters (16.x)

Immutable variables name are set to be in capitalized SNAKE_CASE

Check warning on line 23 in solidity/contracts/AutomationVault.sol

View workflow job for this annotation

GitHub Actions / Run Linters (16.x)

Immutable 'nativeToken' must be in capitalized SNAKE_CASE
/**
* @notice Callers that are approved to call a relay
*/
Expand All @@ -43,8 +44,9 @@ contract AutomationVault is IAutomationVault {
/**
* @param _owner The address of the owner
*/
constructor(address _owner) {
constructor(address _owner, address _nativeToken) {
owner = _owner;
nativeToken = _nativeToken;
}

/// @inheritdoc IAutomationVault
Expand All @@ -58,13 +60,13 @@ contract AutomationVault is IAutomationVault {
}

/// @inheritdoc IAutomationVault
function relays() external view returns (address[] memory _listRelays) {
_listRelays = _relays.values();
function relays() external view returns (address[] memory _relayList) {
_relayList = _relays.values();
}

/// @inheritdoc IAutomationVault
function jobs() external view returns (address[] memory _listJobs) {
_listJobs = _jobs.values();
function jobs() external view returns (address[] memory _jobList) {
_jobList = _jobs.values();
}

/// @inheritdoc IAutomationVault
Expand All @@ -82,10 +84,10 @@ contract AutomationVault is IAutomationVault {

/// @inheritdoc IAutomationVault
function withdrawFunds(address _token, uint256 _amount, address _receiver) external onlyOwner {
// If the token is ETH, transfer the funds to the receiver, otherwise transfer the tokens
if (_token == _ETH) {
// If the token is the native token, transfer the funds to the receiver, otherwise transfer the tokens
if (_token == nativeToken) {
(bool _success,) = _receiver.call{value: _amount}('');
if (!_success) revert AutomationVault_ETHTransferFailed();
if (!_success) revert AutomationVault_NativeTokenTransferFailed();
} else {
IERC20(_token).safeTransfer(_receiver, _amount);
}
Expand Down Expand Up @@ -217,24 +219,24 @@ contract AutomationVault is IAutomationVault {
}

// Create the fee data needed variables
FeeData memory _dataToFee;
FeeData memory _feeInfo;
_dataLength = _feeData.length;
_i = 0;

// Iterate over the fee data to issue the payments
for (_i; _i < _dataLength;) {
_dataToFee = _feeData[_i];
_feeInfo = _feeData[_i];

// If the token is ETH, transfer the funds to the receiver, otherwise transfer the tokens
if (_dataToFee.feeToken == _ETH) {
(_success,) = _dataToFee.feeRecipient.call{value: _dataToFee.fee}('');
if (!_success) revert AutomationVault_ETHTransferFailed();
// If the token is the native token, transfer the funds to the receiver, otherwise transfer the tokens
if (_feeInfo.feeToken == nativeToken) {
(_success,) = _feeInfo.feeRecipient.call{value: _feeInfo.fee}('');
if (!_success) revert AutomationVault_NativeTokenTransferFailed();
} else {
IERC20(_dataToFee.feeToken).safeTransfer(_dataToFee.feeRecipient, _dataToFee.fee);
IERC20(_feeInfo.feeToken).safeTransfer(_feeInfo.feeRecipient, _feeInfo.fee);
}

// Emit the event
emit IssuePayment(msg.sender, _relayCaller, _dataToFee.feeRecipient, _dataToFee.feeToken, _dataToFee.fee);
emit IssuePayment(msg.sender, _relayCaller, _feeInfo.feeRecipient, _feeInfo.feeToken, _feeInfo.fee);

unchecked {
++_i;
Expand All @@ -261,9 +263,9 @@ contract AutomationVault is IAutomationVault {
}

/**
* @notice Fallback function to receive ETH
* @notice Fallback function to receive native tokens
*/
receive() external payable {
emit ETHReceived(msg.sender, msg.value);
emit NativeTokenReceived(msg.sender, msg.value);
}
}
8 changes: 6 additions & 2 deletions solidity/contracts/AutomationVaultFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,13 @@ contract AutomationVaultFactory is IAutomationVaultFactory {
}

/// @inheritdoc IAutomationVaultFactory
function deployAutomationVault(address _owner, uint256 _salt) external returns (IAutomationVault _automationVault) {
function deployAutomationVault(
address _owner,
address _nativeToken,
uint256 _salt
) external returns (IAutomationVault _automationVault) {
// Create the new automation vault with the owner
_automationVault = new AutomationVault{salt: keccak256(abi.encodePacked(msg.sender, _salt))}(_owner);
_automationVault = new AutomationVault{salt: keccak256(abi.encodePacked(msg.sender, _salt))}(_owner, _nativeToken);

// Add the automation vault to the list of automation vaults
_automationVaults.add(address(_automationVault));
Expand Down
15 changes: 11 additions & 4 deletions solidity/interfaces/IAutomationVault.sol
Original file line number Diff line number Diff line change
Expand Up @@ -100,11 +100,11 @@ interface IAutomationVault {
);

/**
* @notice Emitted when ETH is received in the automation vault
* @notice Emitted when native token is received in the automation vault
* @param _sender The sender address
* @param _amount The amount of ETH
* @param _amount The amount of native token
*/
event ETHReceived(address indexed _sender, uint256 _amount);
event NativeTokenReceived(address indexed _sender, uint256 _amount);

/*///////////////////////////////////////////////////////////////
ERRORS
Expand All @@ -113,7 +113,7 @@ interface IAutomationVault {
/**
* @notice Thrown when ether transfer fails
*/
error AutomationVault_ETHTransferFailed();
error AutomationVault_NativeTokenTransferFailed();

/**
* @notice Thrown when a not approved relay caller is trying to execute a job
Expand Down Expand Up @@ -176,6 +176,13 @@ interface IAutomationVault {
*/
function owner() external view returns (address _owner);

/**
* @notice Returns the address of the native token
* @return _nativeToken The address of the native token
*/

function nativeToken() external view returns (address _nativeToken);

/**
* @notice Returns the pending owner address
* @return _pendingOwner The address of the pending owner
Expand Down
7 changes: 6 additions & 1 deletion solidity/interfaces/IAutomationVaultFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,13 @@ interface IAutomationVaultFactory {
/**
* @notice Deploy a new automation vault
* @param _owner The address of the owner
* @param _nativeToken The address of the native token
* @param _salt The salt to use for the automation vault deployment
* @return _automationVault The address of the automation vault deployed
*/
function deployAutomationVault(address _owner, uint256 _salt) external returns (IAutomationVault _automationVault);
function deployAutomationVault(
address _owner,
address _nativeToken,
uint256 _salt
) external returns (IAutomationVault _automationVault);
}
3 changes: 2 additions & 1 deletion solidity/script/Deploy.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {GelatoRelay, IGelatoRelay} from '@contracts/GelatoRelay.sol';
import {Keep3rRelay, IKeep3rRelay} from '@contracts/Keep3rRelay.sol';
import {Keep3rBondedRelay, IKeep3rBondedRelay} from '@contracts/Keep3rBondedRelay.sol';
import {XKeeperMetadata, IXKeeperMetadata} from '@contracts/XKeeperMetadata.sol';
import {_ETH} from '@utils/Constants.sol';

abstract contract Deploy is Script {
// Deployer EOA
Expand Down Expand Up @@ -37,7 +38,7 @@ abstract contract Deploy is Script {
vm.startBroadcast(deployer);

automationVaultFactory = new AutomationVaultFactory();
automationVault = automationVaultFactory.deployAutomationVault(owner, 0);
automationVault = automationVaultFactory.deployAutomationVault(owner, _ETH, 0);

openRelay = new OpenRelay();
gelatoRelay = new GelatoRelay();
Expand Down
19 changes: 10 additions & 9 deletions solidity/test/unit/AutomationVault.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ contract AutomationVaultForTest is AutomationVault {
using EnumerableSet for EnumerableSet.AddressSet;
using EnumerableSet for EnumerableSet.Bytes32Set;

constructor(address _owner) AutomationVault(_owner) {}
constructor(address _owner, address _nativeToken) AutomationVault(_owner, _nativeToken) {}

function setPendingOwnerForTest(address _pendingOwner) public {
pendingOwner = _pendingOwner;
Expand Down Expand Up @@ -87,7 +87,7 @@ abstract contract AutomationVaultUnitTest is Test {
relay = makeAddr('Relay');
relayCaller = makeAddr('RelayCaller');

automationVault = new AutomationVaultForTest(owner);
automationVault = new AutomationVaultForTest(owner, _ETH);
}

function _mockTokenTransfer(address _token) internal {
Expand All @@ -98,6 +98,7 @@ abstract contract AutomationVaultUnitTest is Test {
contract UnitAutomationVaultConstructor is AutomationVaultUnitTest {
function testParamsAreSet() public {
assertEq(automationVault.owner(), owner);
assertEq(automationVault.nativeToken(), _ETH);
}
}

Expand Down Expand Up @@ -200,15 +201,15 @@ contract UnitAutomationVaultWithdrawFunds is AutomationVaultUnitTest {
automationVault.withdrawFunds(_ETH, _amount, owner);
}

function testRevertIfETHTransferFailed(uint160 _amount) public {
function testRevertIfNativeTokenTransferFailed(uint160 _amount) public {
vm.assume(_amount == type(uint160).max);

vm.expectRevert(abi.encodeWithSelector(IAutomationVault.AutomationVault_ETHTransferFailed.selector));
vm.expectRevert(abi.encodeWithSelector(IAutomationVault.AutomationVault_NativeTokenTransferFailed.selector));

automationVault.withdrawFunds(_ETH, _amount, address(automationVault));
}

function testWithdrawETHAmountUpdateBalances(uint128 _amount) public {
function testWithdrawNativeTokenAmountUpdateBalances(uint128 _amount) public {
vm.assume(_amount > 0);

uint256 _balance = address(automationVault).balance;
Expand All @@ -218,7 +219,7 @@ contract UnitAutomationVaultWithdrawFunds is AutomationVaultUnitTest {
assertEq(address(automationVault).balance, _balance - _amount);
}

function testEmitWithdrawETHAmount(uint128 _balance, uint128 _amount) public {
function testEmitWithdrawNativeTokenAmount(uint128 _balance, uint128 _amount) public {
vm.assume(_balance > _amount && _amount > 0);

vm.expectEmit();
Expand Down Expand Up @@ -527,20 +528,20 @@ contract UnitAutomationVaultExec is AutomationVaultUnitTest {
automationVault.exec(relayCaller, _execData, _feeData);
}

function testRevertIfETHTransferFailed(
function testRevertIfNativeTokenTransferFailed(
IAutomationVault.ExecData[] memory _execData,
IAutomationVault.FeeData[] memory _feeData
) public happyPath(_execData, _feeData) {
vm.assume(_feeData.length > 3);
_feeData[1].feeToken = _ETH;
vm.etch(_feeData[1].feeRecipient, type(NoFallbackForTest).runtimeCode);

vm.expectRevert(IAutomationVault.AutomationVault_ETHTransferFailed.selector);
vm.expectRevert(IAutomationVault.AutomationVault_NativeTokenTransferFailed.selector);

automationVault.exec(relayCaller, _execData, _feeData);
}

function testCallETHTransfer(
function testCallNativeTokenTransfer(
IAutomationVault.ExecData[] memory _execData,
IAutomationVault.FeeData[] memory _feeData,
uint128 _fee
Expand Down
46 changes: 33 additions & 13 deletions solidity/test/unit/AutomationVaultFactory.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,19 @@ contract AutomationVaultFactoryForTest is AutomationVaultFactory {
}
}

function preComputeAddressForTest(address _owner, uint256 _salt) public view returns (address _precomputedAddress) {
function preComputeAddressForTest(
address _owner,
address _nativeToken,
uint256 _salt
) public view returns (address _precomputedAddress) {
bytes memory _bytecode = type(AutomationVault).creationCode;

bytes32 _hashed = keccak256(
abi.encodePacked(
bytes1(0xff),
address(this),
keccak256(abi.encodePacked(msg.sender, _salt)),
keccak256(abi.encodePacked(_bytecode, abi.encode(_owner)))
keccak256(abi.encodePacked(_bytecode, abi.encode(_owner, _nativeToken)))
)
);
_precomputedAddress = address(uint160(uint256(_hashed)));
Expand Down Expand Up @@ -93,32 +97,48 @@ contract UnitAutomationVaultFactoryGetAutomationVaults is AutomationVaultFactory
contract UnitAutomationVaultFactoryDeployAutomationVault is AutomationVaultFactoryUnitTest {
address internal _precomputedAddress;

modifier happyPath(address _owner, uint256 _salt) {
_precomputedAddress = automationVaultFactory.preComputeAddressForTest(_owner, _salt);
modifier happyPath(address _owner, address _nativeToken, uint256 _salt) {
_precomputedAddress = automationVaultFactory.preComputeAddressForTest(_owner, _nativeToken, _salt);
_;
}

function testDeployAutomationVault(address _owner, uint256 _salt) public happyPath(_owner, _salt) {
IAutomationVault _automaitonVault = automationVaultFactory.deployAutomationVault(_owner, _salt);
function testDeployAutomationVault(
address _owner,
address _nativeToken,
uint256 _salt
) public happyPath(_owner, _nativeToken, _salt) {
IAutomationVault _automationVault = automationVaultFactory.deployAutomationVault(_owner, _nativeToken, _salt);

// params
assertEq(_automaitonVault.owner(), _owner);
assertEq(_automationVault.owner(), _owner);
}

function testSetAutomationVaults(address _owner, uint256 _salt) public happyPath(_owner, _salt) {
automationVaultFactory.deployAutomationVault(_owner, _salt);
function testSetAutomationVaults(
address _owner,
address _nativeToken,
uint256 _salt
) public happyPath(_owner, _nativeToken, _salt) {
automationVaultFactory.deployAutomationVault(_owner, _nativeToken, _salt);

assertEq(automationVaultFactory.automationVaults(0, 1)[0], _precomputedAddress);
}

function testEmitDeployAutomationVault(address _owner, uint256 _salt) public happyPath(_owner, _salt) {
function testEmitDeployAutomationVault(
address _owner,
address _nativeToken,
uint256 _salt
) public happyPath(_owner, _nativeToken, _salt) {
vm.expectEmit();
emit DeployAutomationVault(_owner, _precomputedAddress);

automationVaultFactory.deployAutomationVault(_owner, _salt);
automationVaultFactory.deployAutomationVault(_owner, _nativeToken, _salt);
}

function testReturnAutomationVault(address _owner, uint256 _salt) public happyPath(_owner, _salt) {
assertEq(address(automationVaultFactory.deployAutomationVault(_owner, _salt)), _precomputedAddress);
function testReturnAutomationVault(
address _owner,
address _nativeToken,
uint256 _salt
) public happyPath(_owner, _nativeToken, _salt) {
assertEq(address(automationVaultFactory.deployAutomationVault(_owner, _nativeToken, _salt)), _precomputedAddress);
}
}
Loading

0 comments on commit 7159d89

Please sign in to comment.