Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

test: basic flow content #14

Merged
merged 7 commits into from
Aug 12, 2023
99 changes: 99 additions & 0 deletions contracts/src/interfaces/IGauge.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IGauge {
error NotAlive();
error NotAuthorized();
error NotVoter();
error RewardRateTooHigh();
error ZeroAmount();
error ZeroRewardRate();

event Deposit(address indexed from, address indexed to, uint256 amount);
event Withdraw(address indexed from, uint256 amount);
event NotifyReward(address indexed from, uint256 amount);
event ClaimFees(address indexed from, uint256 claimed0, uint256 claimed1);
event ClaimRewards(address indexed from, uint256 amount);

/// @notice Address of the pool LP token which is deposited (staked) for rewards
function stakingToken() external view returns (address);

/// @notice Address of the token (VELO v2) rewarded to stakers
function rewardToken() external view returns (address);

/// @notice Address of the FeesVotingReward contract linked to the gauge
function feesVotingReward() external view returns (address);

/// @notice Address of Velodrome v2 Voter
function voter() external view returns (address);

/// @notice Returns if gauge is linked to a legitimate Velodrome pool
function isPool() external view returns (bool);

/// @notice Timestamp end of current rewards period
function periodFinish() external view returns (uint256);

/// @notice Current reward rate of rewardToken to distribute per second
function rewardRate() external view returns (uint256);

/// @notice Most recent timestamp contract has updated state
function lastUpdateTime() external view returns (uint256);

/// @notice Most recent stored value of rewardPerToken
function rewardPerTokenStored() external view returns (uint256);

/// @notice Amount of stakingToken deposited for rewards
function totalSupply() external view returns (uint256);

/// @notice Get the amount of stakingToken deposited by an account
function balanceOf(address) external view returns (uint256);

/// @notice Cached rewardPerTokenStored for an account based on their most recent action
function userRewardPerTokenPaid(address) external view returns (uint256);

/// @notice Cached amount of rewardToken earned for an account
function rewards(address) external view returns (uint256);

/// @notice View to see the rewardRate given the timestamp of the start of the epoch
function rewardRateByEpoch(uint256) external view returns (uint256);

/// @notice Cached amount of fees generated from the Pool linked to the Gauge of token0
function fees0() external view returns (uint256);

/// @notice Cached amount of fees generated from the Pool linked to the Gauge of token1
function fees1() external view returns (uint256);

/// @notice Get the current reward rate per unit of stakingToken deposited
function rewardPerToken() external view returns (uint256 _rewardPerToken);

/// @notice Returns the last time the reward was modified or periodFinish if the reward has ended
function lastTimeRewardApplicable() external view returns (uint256 _time);

/// @notice Returns accrued balance to date from last claim / first deposit.
function earned(address _account) external view returns (uint256 _earned);

/// @notice Total amount of rewardToken to distribute for the current rewards period
function left() external view returns (uint256 _left);

/// @notice Retrieve rewards for an address.
/// @dev Throws if not called by same address or voter.
/// @param _account .
function getReward(address _account) external;

/// @notice Deposit LP tokens into gauge for msg.sender
/// @param _amount .
function deposit(uint256 _amount) external;

/// @notice Deposit LP tokens into gauge for any user
/// @param _amount .
/// @param _recipient Recipient to give balance to
function deposit(uint256 _amount, address _recipient) external;

/// @notice Withdraw LP tokens for user
/// @param _amount .
function withdraw(uint256 _amount) external;

/// @dev Notifies gauge of gauge rewards. Assumes gauge reward tokens is 18 decimals.
/// If not 18 decimals, rewardRate may have rounding issues.
function notifyRewardAmount(uint256 amount) external;
}
78 changes: 66 additions & 12 deletions contracts/src/modules/DummyModule.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@
pragma solidity ^0.8.20;

import {ISafe} from "safe-protocol/interfaces/Accounts.sol";
import {ISafeProtocolManager, SafeRootAccess} from "safe-protocol/interfaces/Manager.sol";
import {ISafeProtocolManager} from "safe-protocol/interfaces/Manager.sol";
import {SafeTransaction, SafeProtocolAction} from "safe-protocol/DataTypes.sol";

import {BaseModule, PluginMetadata} from "./BaseModule.sol";

import {IGauge} from "../interfaces/IGauge.sol";

contract DummyModule is BaseModule {
////////////////////////////////////////////////////////////////////////////
// STRUCT
Expand All @@ -24,11 +27,43 @@ contract DummyModule is BaseModule {
// address (SafeProtocolManager)
address manager;

// address (relayer: keeper, gelato, AA)
address public relayer;

// address (Safe address) => DummyConfig
mapping(address => DummyConfig) public safeConfigs;

constructor(address _manager, PluginMetadata memory _data) BaseModule(_data) {
////////////////////////////////////////////////////////////////////////////
// ERRORS
////////////////////////////////////////////////////////////////////////////

error UntrustedRelayer(address origin);

error TooSoon(uint256 currentTime, uint256 updateTime, uint256 minDuration);

////////////////////////////////////////////////////////////////////////////
// MODIFIERS
////////////////////////////////////////////////////////////////////////////

modifier onlyRelayer() {
if (msg.sender != relayer) revert UntrustedRelayer(msg.sender);
_;
}

////////////////////////////////////////////////////////////////////////////
// EVENTS
////////////////////////////////////////////////////////////////////////////

/// @dev use for subgraph to display basic info in ui as per `safe` basis
event PluginTransactionExec(address safe, address gauge, uint256 timestamp);
petrovska-petro marked this conversation as resolved.
Show resolved Hide resolved

constructor(
address _manager,
petrovska-petro marked this conversation as resolved.
Show resolved Hide resolved
address _relayer,
PluginMetadata memory _data
) BaseModule(_data) {
manager = _manager;
relayer = _relayer;
}

////////////////////////////////////////////////////////////////////////////
Expand All @@ -39,15 +74,34 @@ contract DummyModule is BaseModule {
safeConfigs[_safe] = _config;
}

/// @notice Executes a Safe transaction
/// @param _manager Address of the Safe{Core} Protocol Manager.
/// @param _safe Safe account
/// @param _rootAccess Contains the set of actions to be done in the Safe transaction
function executeFromPlugin(
ISafeProtocolManager _manager,
ISafe _safe,
SafeRootAccess calldata _rootAccess
) external {
_manager.executeRootAccess(_safe, _rootAccess);
/// @notice Executes a Safe transaction. Only executable by trusted relayer
/// @param _safe Safe account target address
function executeFromPlugin(ISafe _safe) external onlyRelayer {
DummyConfig storage config = safeConfigs[address(_safe)];

uint256 lastCallTimestampCached = config.lastCall;
uint256 cadenceCached = config.cadenceSec;

if ((block.timestamp - lastCallTimestampCached) < cadenceCached) {
revert TooSoon(block.timestamp, lastCallTimestampCached, cadenceCached);
}

SafeProtocolAction[] memory transactions = new SafeProtocolAction[](1);
transactions[0] = SafeProtocolAction({
to: payable(config.vault),
value: 0,
data: abi.encodeWithSelector(IGauge.getReward.selector, address(_safe))
});

SafeTransaction memory transaction = SafeTransaction({
actions: transactions,
nonce: 0,
metadataHash: bytes32(0)
});

ISafeProtocolManager(manager).executeTransaction(_safe, transaction);
config.lastCall = uint64(block.timestamp);

emit PluginTransactionExec(address(_safe), config.vault, block.timestamp);
}
}
44 changes: 44 additions & 0 deletions contracts/src/test/MockGauge.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

contract MockGauge is ERC20 {
address public lpToken;

address[] public rewardTokens;

event RewardClaimed(address acccount, uint256 amount, uint256 timestamp);

constructor(
string memory _name,
string memory _symbol,
address _lpToken,
address[] memory _rewardTokens
) ERC20(_name, _symbol) {
lpToken = _lpToken;
rewardTokens = _rewardTokens;
rewardTokens.push(address(0));
}

function deposit(uint256 amount) external {
_mint(msg.sender, amount);
IERC20(lpToken).transferFrom(msg.sender, address(this), amount);
}

function withdraw(uint256 amount) external {
_burn(msg.sender, amount);
IERC20(lpToken).transfer(msg.sender, amount);
}

function getReward(address _account) external {
uint256 amount = balanceOf(msg.sender);

for (uint256 i = 0; i < rewardTokens.length; i++) {
if (rewardTokens[i] == address(0)) break;
IERC20(rewardTokens[i]).transfer(_account, amount);
emit RewardClaimed(_account, amount, block.timestamp);
}
}
}
Loading