Commit 0ceff8b8 authored by smartcontracts's avatar smartcontracts Committed by GitHub

fix(ctp): Drippie Spearbit audit fixes (#3280)

* fix(ctp): Drippie Spearbit issue 45

Fixes Spearbit issue 45, saves gas by using calldata parameters instead
of memory parameters.

* fix(ctp): Drippie Spearbit issue 44

Fixes Spearbit issue 44, documents the count variable and increments
count before external calls.

* fix(ctp): Drippie Spearbit issue 42

Fixes Spearbit issue 42, saves gas by removing extra SLOADs.

* fix(ctp): Drippie Spearbit issue 35

Fixes Spearbit issue 35, corrects contract layout ordering.

* fix(ctp): Drippie Spearbit issue 34

Fixes Spearbit issue 34, adds natspec where incomplete.

* fix(ctp): Drippie Spearbit issue 32 and 33

Fixes Spearbit issues 32 and 33, clarifies the behavior of the
executable function to revert instead of returning false, and removes an
unnecessary check as a result.

* fix(ctp): Drippie Spearbit issue 31

Fixes Spearbit issue 31, requires explicit opt-in for reentrant drip
execution.

* fix(ctp): Drippie Spearbit issue 28

Fixes Spearbit issue 28, better documentation of the behavior of
execution checks in the drip function.

* fix(ctp): Drippie Spearbit issue 21

Fixes Spearbit issue 21, use MIT licensed version of Solmate.

* fix(ctp): Drippie Spearbit issue 25

Fixes Spearbit issue 25, reorders DripStatus enum for clarity.

* fix(ctp): Drippie Spearbit issue 24

Fixes Spearbit issue 24, use call with value over transfer to avoid
future gas issues.

* fix(ctp): Drippie Spearbit issue 22

Fixes Spearbit issue 22, removes unnecessary gas parameter.

* fix(ctp): Drippie Spearbit issue 39

Fixes Spearbit issue 39, updates Solidity to latest version.
Co-authored-by: default avatarMark Tyneway <mark.tyneway@gmail.com>
parent af3e56b1
---
'@eth-optimism/contracts-periphery': patch
---
Drippie Spearbit audit fix for issues #32 and #33, clarify behavior of executable function
---
'@eth-optimism/contracts-periphery': patch
'@eth-optimism/drippie-mon': patch
---
Drippie Spearbit audit fix for issue #25, reorder DripStatus enum for clarity
---
'@eth-optimism/contracts-periphery': patch
---
Drippie Spearbit audit fix for issue #44, document drip count and increment before external calls
---
'@eth-optimism/contracts-periphery': patch
---
Drippie Spearbit audit fix for issue 24, use call over transfer for withdrawETH
---
'@eth-optimism/contracts-periphery': patch
---
Drippie Spearbit audit fix for issue 22, remove unnecessary gas parameter
---
'@eth-optimism/contracts-periphery': patch
---
Drippie Spearbit audit fix for issue #34, missing natspec
---
'@eth-optimism/contracts-periphery': patch
---
Drippie Spearbit audit fix for issue #28, document dripcheck behavior in drip function
---
'@eth-optimism/contracts-periphery': patch
---
Drippie Spearbit audit fix #42, remove unnecessary SLOADs in the status function
---
'@eth-optimism/contracts-periphery': patch
---
Drippie Spearbit audit fix for issue #39, update to latest version of Solidity
---
'@eth-optimism/contracts-periphery': patch
---
Drippie Spearbit audit fix for issue #21, use correct version of Solmate
---
'@eth-optimism/contracts-periphery': patch
---
Drippie Spearbit audit fix for issue #31, require explicit opt-in for reentrant drips
---
'@eth-optimism/contracts-periphery': patch
---
Drippie Spearbit audit fix for issue #45, calldata over memory to save gas
---
'@eth-optimism/contracts-periphery': patch
---
Drippie Spearbit audit fix for issue #35, correct contract layout ordering
...@@ -15,6 +15,7 @@ contract AssetReceiver is Transactor { ...@@ -15,6 +15,7 @@ contract AssetReceiver is Transactor {
* @notice Emitted when ETH is received by this address. * @notice Emitted when ETH is received by this address.
* *
* @param from Address that sent ETH to this contract. * @param from Address that sent ETH to this contract.
* @param amount Amount of ETH received.
*/ */
event ReceivedETH(address indexed from, uint256 amount); event ReceivedETH(address indexed from, uint256 amount);
...@@ -86,7 +87,7 @@ contract AssetReceiver is Transactor { ...@@ -86,7 +87,7 @@ contract AssetReceiver is Transactor {
*/ */
function withdrawETH(address payable _to, uint256 _amount) public onlyOwner { function withdrawETH(address payable _to, uint256 _amount) public onlyOwner {
// slither-disable-next-line reentrancy-unlimited-gas // slither-disable-next-line reentrancy-unlimited-gas
_to.transfer(_amount); (bool success, ) = _to.call{ value: _amount }("");
emit WithdrewETH(msg.sender, _to, _amount); emit WithdrewETH(msg.sender, _to, _amount);
} }
......
...@@ -18,7 +18,6 @@ contract Transactor is Owned { ...@@ -18,7 +18,6 @@ contract Transactor is Owned {
* *
* @param _target Address to call. * @param _target Address to call.
* @param _data Data to send with the call. * @param _data Data to send with the call.
* @param _gas Amount of gas to send with the call.
* @param _value ETH value to send with the call. * @param _value ETH value to send with the call.
* *
* @return Boolean success value. * @return Boolean success value.
...@@ -27,10 +26,9 @@ contract Transactor is Owned { ...@@ -27,10 +26,9 @@ contract Transactor is Owned {
function CALL( function CALL(
address _target, address _target,
bytes memory _data, bytes memory _data,
uint256 _gas,
uint256 _value uint256 _value
) external payable onlyOwner returns (bool, bytes memory) { ) external payable onlyOwner returns (bool, bytes memory) {
return _target.call{ gas: _gas, value: _value }(_data); return _target.call{ value: _value }(_data);
} }
/** /**
...@@ -38,17 +36,17 @@ contract Transactor is Owned { ...@@ -38,17 +36,17 @@ contract Transactor is Owned {
* *
* @param _target Address to call. * @param _target Address to call.
* @param _data Data to send with the call. * @param _data Data to send with the call.
* @param _gas Amount of gas to send with the call.
* *
* @return Boolean success value. * @return Boolean success value.
* @return Bytes data returned by the call. * @return Bytes data returned by the call.
*/ */
function DELEGATECALL( function DELEGATECALL(address _target, bytes memory _data)
address _target, external
bytes memory _data, payable
uint256 _gas onlyOwner
) external payable onlyOwner returns (bool, bytes memory) { returns (bool, bytes memory)
{
// slither-disable-next-line controlled-delegatecall // slither-disable-next-line controlled-delegatecall
return _target.delegatecall{ gas: _gas }(_data); return _target.delegatecall(_data);
} }
} }
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity 0.8.15; pragma solidity 0.8.16;
import { AssetReceiver } from "../AssetReceiver.sol"; import { AssetReceiver } from "../AssetReceiver.sol";
import { IDripCheck } from "./IDripCheck.sol"; import { IDripCheck } from "./IDripCheck.sol";
...@@ -21,14 +21,14 @@ contract Drippie is AssetReceiver { ...@@ -21,14 +21,14 @@ contract Drippie is AssetReceiver {
* @notice Enum representing different status options for a given drip. * @notice Enum representing different status options for a given drip.
* *
* @custom:value NONE Drip does not exist. * @custom:value NONE Drip does not exist.
* @custom:value ACTIVE Drip is active and can be executed.
* @custom:value PAUSED Drip is paused and cannot be executed until reactivated. * @custom:value PAUSED Drip is paused and cannot be executed until reactivated.
* @custom:value ACTIVE Drip is active and can be executed.
* @custom:value ARCHIVED Drip is archived and can no longer be executed or reactivated. * @custom:value ARCHIVED Drip is archived and can no longer be executed or reactivated.
*/ */
enum DripStatus { enum DripStatus {
NONE, NONE,
ACTIVE,
PAUSED, PAUSED,
ACTIVE,
ARCHIVED ARCHIVED
} }
...@@ -45,6 +45,7 @@ contract Drippie is AssetReceiver { ...@@ -45,6 +45,7 @@ contract Drippie is AssetReceiver {
* @notice Represents the configuration for a given drip. * @notice Represents the configuration for a given drip.
*/ */
struct DripConfig { struct DripConfig {
bool reentrant;
uint256 interval; uint256 interval;
IDripCheck dripcheck; IDripCheck dripcheck;
bytes checkparams; bytes checkparams;
...@@ -123,7 +124,7 @@ contract Drippie is AssetReceiver { ...@@ -123,7 +124,7 @@ contract Drippie is AssetReceiver {
* @param _name Name of the drip. * @param _name Name of the drip.
* @param _config Configuration for the drip. * @param _config Configuration for the drip.
*/ */
function create(string memory _name, DripConfig memory _config) external onlyOwner { function create(string calldata _name, DripConfig calldata _config) external onlyOwner {
// Make sure this drip doesn't already exist. We *must* guarantee that no other function // Make sure this drip doesn't already exist. We *must* guarantee that no other function
// will ever set the status of a drip back to NONE after it's been created. This is why // will ever set the status of a drip back to NONE after it's been created. This is why
// archival is a separate status. // archival is a separate status.
...@@ -132,9 +133,25 @@ contract Drippie is AssetReceiver { ...@@ -132,9 +133,25 @@ contract Drippie is AssetReceiver {
"Drippie: drip with that name already exists" "Drippie: drip with that name already exists"
); );
// Validate the drip interval, only allowing an interval of zero if the drip has explicitly
// been marked as reentrant. Prevents client-side bugs making a drip infinitely executable
// within the same block (of course, restricted by gas limits).
if (_config.reentrant) {
require(
_config.interval == 0,
"Drippie: if allowing reentrant drip, must set interval to zero"
);
} else {
require(
_config.interval > 0,
"Drippie: interval must be greater than zero if drip is not reentrant"
);
}
// We initialize this way because Solidity won't let us copy arrays into storage yet. // We initialize this way because Solidity won't let us copy arrays into storage yet.
DripState storage state = drips[_name]; DripState storage state = drips[_name];
state.status = DripStatus.PAUSED; state.status = DripStatus.PAUSED;
state.config.reentrant = _config.reentrant;
state.config.interval = _config.interval; state.config.interval = _config.interval;
state.config.dripcheck = _config.dripcheck; state.config.dripcheck = _config.dripcheck;
state.config.checkparams = _config.checkparams; state.config.checkparams = _config.checkparams;
...@@ -157,7 +174,7 @@ contract Drippie is AssetReceiver { ...@@ -157,7 +174,7 @@ contract Drippie is AssetReceiver {
* @param _name Name of the drip to update. * @param _name Name of the drip to update.
* @param _status New drip status. * @param _status New drip status.
*/ */
function status(string memory _name, DripStatus _status) external onlyOwner { function status(string calldata _name, DripStatus _status) external onlyOwner {
// Make sure we can never set drip status back to NONE. A simple security measure to // Make sure we can never set drip status back to NONE. A simple security measure to
// prevent accidental overwrites if this code is ever updated down the line. // prevent accidental overwrites if this code is ever updated down the line.
require( require(
...@@ -165,27 +182,30 @@ contract Drippie is AssetReceiver { ...@@ -165,27 +182,30 @@ contract Drippie is AssetReceiver {
"Drippie: drip status can never be set back to NONE after creation" "Drippie: drip status can never be set back to NONE after creation"
); );
// Load the drip status once to avoid unnecessary SLOADs.
DripStatus curr = drips[_name].status;
// Make sure the drip in question actually exists. Not strictly necessary but there doesn't // Make sure the drip in question actually exists. Not strictly necessary but there doesn't
// seem to be any clear reason why you would want to do this, and it may save some gas in // seem to be any clear reason why you would want to do this, and it may save some gas in
// the case of a front-end bug. // the case of a front-end bug.
require( require(
drips[_name].status != DripStatus.NONE, curr != DripStatus.NONE,
"Drippie: drip with that name does not exist" "Drippie: drip with that name does not exist and cannot be updated"
); );
// Once a drip has been archived, it cannot be un-archived. This is, after all, the entire // Once a drip has been archived, it cannot be un-archived. This is, after all, the entire
// point of archiving a drip. // point of archiving a drip.
require( require(
drips[_name].status != DripStatus.ARCHIVED, curr != DripStatus.ARCHIVED,
"Drippie: drip with that name has been archived" "Drippie: drip with that name has been archived and cannot be updated"
); );
// Although not strictly necessary, we make sure that the status here is actually changing. // Although not strictly necessary, we make sure that the status here is actually changing.
// This may save the client some gas if there's a front-end bug and the user accidentally // This may save the client some gas if there's a front-end bug and the user accidentally
// tries to "change" the status to the same value as before. // tries to "change" the status to the same value as before.
require( require(
drips[_name].status != _status, curr != _status,
"Drippie: cannot set drip status to same status as before" "Drippie: cannot set drip status to the same status as its current status"
); );
// If the user is trying to archive this drip, make sure the drip has been paused. We do // If the user is trying to archive this drip, make sure the drip has been paused. We do
...@@ -193,14 +213,14 @@ contract Drippie is AssetReceiver { ...@@ -193,14 +213,14 @@ contract Drippie is AssetReceiver {
// abundantly clear. // abundantly clear.
if (_status == DripStatus.ARCHIVED) { if (_status == DripStatus.ARCHIVED) {
require( require(
drips[_name].status == DripStatus.PAUSED, curr == DripStatus.PAUSED,
"Drippie: drip must be paused to be archived" "Drippie: drip must first be paused before being archived"
); );
} }
// If we made it here then we can safely update the status. // If we made it here then we can safely update the status.
drips[_name].status = _status; drips[_name].status = _status;
emit DripStatusUpdated(_name, _name, drips[_name].status); emit DripStatusUpdated(_name, _name, _status);
} }
/** /**
...@@ -208,9 +228,9 @@ contract Drippie is AssetReceiver { ...@@ -208,9 +228,9 @@ contract Drippie is AssetReceiver {
* *
* @param _name Drip to check. * @param _name Drip to check.
* *
* @return True if the drip is executable, false otherwise. * @return True if the drip is executable, reverts otherwise.
*/ */
function executable(string memory _name) public view returns (bool) { function executable(string calldata _name) public view returns (bool) {
DripState storage state = drips[_name]; DripState storage state = drips[_name];
// Only allow active drips to be executed, an obvious security measure. // Only allow active drips to be executed, an obvious security measure.
...@@ -245,18 +265,18 @@ contract Drippie is AssetReceiver { ...@@ -245,18 +265,18 @@ contract Drippie is AssetReceiver {
* signal that the drip should be executable according to the drip parameters, drip * signal that the drip should be executable according to the drip parameters, drip
* check, and drip interval. Note that drip parameters are read entirely from the state * check, and drip interval. Note that drip parameters are read entirely from the state
* and are not supplied as user input, so there should not be any way for a * and are not supplied as user input, so there should not be any way for a
* non-authorized user to influence the behavior of the drip. * non-authorized user to influence the behavior of the drip. Note that the drip check
* is executed only **once** at the beginning of the call to the drip function and will
* not be executed again between the drip actions within this call.
* *
* @param _name Name of the drip to trigger. * @param _name Name of the drip to trigger.
*/ */
function drip(string memory _name) external { function drip(string calldata _name) external {
DripState storage state = drips[_name]; DripState storage state = drips[_name];
// Make sure the drip can be executed. // Make sure the drip can be executed. Since executable reverts if the drip is not ready to
require( // be executed, we don't need to do an assertion that the returned value is true.
executable(_name) == true, executable(_name);
"Drippie: drip cannot be executed at this time, try again later"
);
// Update the last execution time for this drip before the call. Note that it's entirely // Update the last execution time for this drip before the call. Note that it's entirely
// possible for a drip to be executed multiple times per block or even multiple times // possible for a drip to be executed multiple times per block or even multiple times
...@@ -265,6 +285,11 @@ contract Drippie is AssetReceiver { ...@@ -265,6 +285,11 @@ contract Drippie is AssetReceiver {
// block (since this will then prevent re-entrancy). // block (since this will then prevent re-entrancy).
state.last = block.timestamp; state.last = block.timestamp;
// Update the number of times this drip has been executed. Although this increases the cost
// of using Drippie, it slightly simplifies the client-side by not having to worry about
// counting drips via events. Useful for monitoring the rate of drip execution.
state.count++;
// Execute each action in the drip. We allow drips to have multiple actions because there // Execute each action in the drip. We allow drips to have multiple actions because there
// are scenarios in which a contract must do multiple things atomically. For example, the // are scenarios in which a contract must do multiple things atomically. For example, the
// contract may need to withdraw ETH from one account and then deposit that ETH into // contract may need to withdraw ETH from one account and then deposit that ETH into
...@@ -297,7 +322,6 @@ contract Drippie is AssetReceiver { ...@@ -297,7 +322,6 @@ contract Drippie is AssetReceiver {
); );
} }
state.count++;
emit DripExecuted(_name, _name, msg.sender, block.timestamp); emit DripExecuted(_name, _name, msg.sender, block.timestamp);
} }
} }
...@@ -7,5 +7,12 @@ interface IDripCheck { ...@@ -7,5 +7,12 @@ interface IDripCheck {
// possible to easily encode parameters on the client side. Solidity does not support generics // possible to easily encode parameters on the client side. Solidity does not support generics
// so it's not possible to do this with explicit typing. // so it's not possible to do this with explicit typing.
/**
* @notice Checks whether a drip should be executable.
*
* @param _params Encoded parameters for the drip check.
*
* @return Whether the drip should be executed.
*/
function check(bytes memory _params) external view returns (bool); function check(bytes memory _params) external view returns (bool);
} }
...@@ -8,12 +8,21 @@ import { IDripCheck } from "../IDripCheck.sol"; ...@@ -8,12 +8,21 @@ import { IDripCheck } from "../IDripCheck.sol";
* @notice DripCheck for checking if an account's balance is above a given threshold. * @notice DripCheck for checking if an account's balance is above a given threshold.
*/ */
contract CheckBalanceHigh is IDripCheck { contract CheckBalanceHigh is IDripCheck {
event _EventToExposeStructInABI__Params(Params params);
struct Params { struct Params {
address target; address target;
uint256 threshold; uint256 threshold;
} }
/**
* @notice External event used to help client-side tooling encode parameters.
*
* @param params Parameters to encode.
*/
event _EventToExposeStructInABI__Params(Params params);
/**
* @inheritdoc IDripCheck
*/
function check(bytes memory _params) external view returns (bool) { function check(bytes memory _params) external view returns (bool) {
Params memory params = abi.decode(_params, (Params)); Params memory params = abi.decode(_params, (Params));
......
...@@ -8,12 +8,21 @@ import { IDripCheck } from "../IDripCheck.sol"; ...@@ -8,12 +8,21 @@ import { IDripCheck } from "../IDripCheck.sol";
* @notice DripCheck for checking if an account's balance is below a given threshold. * @notice DripCheck for checking if an account's balance is below a given threshold.
*/ */
contract CheckBalanceLow is IDripCheck { contract CheckBalanceLow is IDripCheck {
event _EventToExposeStructInABI__Params(Params params);
struct Params { struct Params {
address target; address target;
uint256 threshold; uint256 threshold;
} }
/**
* @notice External event used to help client-side tooling encode parameters.
*
* @param params Parameters to encode.
*/
event _EventToExposeStructInABI__Params(Params params);
/**
* @inheritdoc IDripCheck
*/
function check(bytes memory _params) external view returns (bool) { function check(bytes memory _params) external view returns (bool) {
Params memory params = abi.decode(_params, (Params)); Params memory params = abi.decode(_params, (Params));
......
...@@ -12,13 +12,22 @@ interface IGelatoTreasury { ...@@ -12,13 +12,22 @@ interface IGelatoTreasury {
* @notice DripCheck for checking if an account's Gelato ETH balance is below some threshold. * @notice DripCheck for checking if an account's Gelato ETH balance is below some threshold.
*/ */
contract CheckGelatoLow is IDripCheck { contract CheckGelatoLow is IDripCheck {
event _EventToExposeStructInABI__Params(Params params);
struct Params { struct Params {
address treasury; address treasury;
uint256 threshold; uint256 threshold;
address recipient; address recipient;
} }
/**
* @notice External event used to help client-side tooling encode parameters.
*
* @param params Parameters to encode.
*/
event _EventToExposeStructInABI__Params(Params params);
/**
* @inheritdoc IDripCheck
*/
function check(bytes memory _params) external view returns (bool) { function check(bytes memory _params) external view returns (bool) {
Params memory params = abi.decode(_params, (Params)); Params memory params = abi.decode(_params, (Params));
......
...@@ -8,6 +8,9 @@ import { IDripCheck } from "../IDripCheck.sol"; ...@@ -8,6 +8,9 @@ import { IDripCheck } from "../IDripCheck.sol";
* @notice DripCheck that always returns true. * @notice DripCheck that always returns true.
*/ */
contract CheckTrue is IDripCheck { contract CheckTrue is IDripCheck {
/**
* @inheritdoc IDripCheck
*/
function check(bytes memory) external pure returns (bool) { function check(bytes memory) external pure returns (bool) {
return true; return true;
} }
......
...@@ -126,6 +126,12 @@ const config: HardhatUserConfig = { ...@@ -126,6 +126,12 @@ const config: HardhatUserConfig = {
}, },
solidity: { solidity: {
compilers: [ compilers: [
{
version: '0.8.16',
settings: {
optimizer: { enabled: true, runs: 10_000 },
},
},
{ {
version: '0.8.15', version: '0.8.15',
settings: { settings: {
......
...@@ -62,7 +62,7 @@ ...@@ -62,7 +62,7 @@
"@nomiclabs/hardhat-ethers": "^2.0.2", "@nomiclabs/hardhat-ethers": "^2.0.2",
"@nomiclabs/hardhat-etherscan": "^3.0.3", "@nomiclabs/hardhat-etherscan": "^3.0.3",
"@nomiclabs/hardhat-waffle": "^2.0.3", "@nomiclabs/hardhat-waffle": "^2.0.3",
"@rari-capital/solmate": "https://github.com/rari-capital/solmate.git#eaaccf88ac5290299884437e1aee098a96583d54", "@rari-capital/solmate": "7.0.0-alpha.3",
"@openzeppelin/contracts": "4.6.0", "@openzeppelin/contracts": "4.6.0",
"@openzeppelin/contracts-upgradeable": "4.7.1", "@openzeppelin/contracts-upgradeable": "4.7.1",
"@types/chai": "^4.2.18", "@types/chai": "^4.2.18",
......
...@@ -7,6 +7,7 @@ import { HardhatRuntimeEnvironment } from 'hardhat/types' ...@@ -7,6 +7,7 @@ import { HardhatRuntimeEnvironment } from 'hardhat/types'
import { Etherscan } from '../etherscan' import { Etherscan } from '../etherscan'
export interface DripConfig { export interface DripConfig {
reentrant?: boolean
interval: BigNumberish interval: BigNumberish
dripcheck: string dripcheck: string
checkparams?: any checkparams?: any
...@@ -113,6 +114,7 @@ export const parseDrippieConfig = async ( ...@@ -113,6 +114,7 @@ export const parseDrippieConfig = async (
} }
dripConfig.interval = ethers.BigNumber.from(dripConfig.interval) dripConfig.interval = ethers.BigNumber.from(dripConfig.interval)
dripConfig.reentrant = dripConfig.reentrant || false
} }
return parsed as Required<DrippieConfig> return parsed as Required<DrippieConfig>
......
...@@ -28,7 +28,7 @@ describe('Transactor', () => { ...@@ -28,7 +28,7 @@ describe('Transactor', () => {
describe('when called by authorized address', () => { describe('when called by authorized address', () => {
it('should do a call to the target contract', async () => { it('should do a call to the target contract', async () => {
const data = CallRecorder.interface.encodeFunctionData('record') const data = CallRecorder.interface.encodeFunctionData('record')
await Transactor.CALL(CallRecorder.address, data, 1_000_000, 0, { await Transactor.CALL(CallRecorder.address, data, 0, {
gasLimit: 2_000_000, gasLimit: 2_000_000,
}) })
...@@ -40,7 +40,7 @@ describe('Transactor', () => { ...@@ -40,7 +40,7 @@ describe('Transactor', () => {
it('should be able to call with value', async () => { it('should be able to call with value', async () => {
const data = CallRecorder.interface.encodeFunctionData('record') const data = CallRecorder.interface.encodeFunctionData('record')
const value = 69 const value = 69
await Transactor.CALL(CallRecorder.address, data, 1_000_000, value, { await Transactor.CALL(CallRecorder.address, data, value, {
gasLimit: 2_000_000, gasLimit: 2_000_000,
value, value,
}) })
...@@ -48,32 +48,15 @@ describe('Transactor', () => { ...@@ -48,32 +48,15 @@ describe('Transactor', () => {
const call = await CallRecorder.lastCall() const call = await CallRecorder.lastCall()
expect(call.value).to.equal(value) expect(call.value).to.equal(value)
}) })
it('should be able to set gas limit', async () => {
const data = CallRecorder.interface.encodeFunctionData('record')
const gas = 100_000
await Transactor.CALL(CallRecorder.address, data, gas, 0, {
gasLimit: 2_000_000,
})
const call = await CallRecorder.lastCall()
expect(call.gas.toNumber()).to.be.lessThan(gas)
})
}) })
describe('when called by not authorized address', () => { describe('when called by not authorized address', () => {
it('should be reverted', async () => { it('should be reverted', async () => {
const data = CallRecorder.interface.encodeFunctionData('record') const data = CallRecorder.interface.encodeFunctionData('record')
await expect( await expect(
Transactor.connect(signer2).CALL( Transactor.connect(signer2).CALL(CallRecorder.address, data, 0, {
CallRecorder.address,
data,
1_000_000,
0,
{
gasLimit: 2_000_000, gasLimit: 2_000_000,
} })
)
).to.be.revertedWith('UNAUTHORIZED') ).to.be.revertedWith('UNAUTHORIZED')
}) })
}) })
...@@ -86,7 +69,6 @@ describe('Transactor', () => { ...@@ -86,7 +69,6 @@ describe('Transactor', () => {
const ret = await Transactor.callStatic.DELEGATECALL( const ret = await Transactor.callStatic.DELEGATECALL(
Reverter.address, Reverter.address,
data, data,
1_000_000,
{ {
gasLimit: 2_000_000, gasLimit: 2_000_000,
} }
...@@ -101,14 +83,9 @@ describe('Transactor', () => { ...@@ -101,14 +83,9 @@ describe('Transactor', () => {
it('should be reverted', async () => { it('should be reverted', async () => {
const data = Reverter.interface.encodeFunctionData('doRevert') const data = Reverter.interface.encodeFunctionData('doRevert')
await expect( await expect(
Transactor.connect(signer2).DELEGATECALL( Transactor.connect(signer2).DELEGATECALL(Reverter.address, data, {
Reverter.address,
data,
1_000_000,
{
gasLimit: 2_000_000, gasLimit: 2_000_000,
} })
)
).to.be.revertedWith('UNAUTHORIZED') ).to.be.revertedWith('UNAUTHORIZED')
}) })
}) })
......
...@@ -60,7 +60,7 @@ describe('Drippie', () => { ...@@ -60,7 +60,7 @@ describe('Drippie', () => {
).to.emit(Drippie, 'DripCreated') ).to.emit(Drippie, 'DripCreated')
const drip = await Drippie.drips(DEFAULT_DRIP_NAME) const drip = await Drippie.drips(DEFAULT_DRIP_NAME)
expect(drip.status).to.equal(2) // PAUSED expect(drip.status).to.equal(1) // PAUSED
expect(drip.last).to.deep.equal(hre.ethers.BigNumber.from(0)) expect(drip.last).to.deep.equal(hre.ethers.BigNumber.from(0))
expect(drip.config.interval).to.deep.equal(DEFAULT_DRIP_CONFIG.interval) expect(drip.config.interval).to.deep.equal(DEFAULT_DRIP_CONFIG.interval)
expect(drip.config.dripcheck).to.deep.equal( expect(drip.config.dripcheck).to.deep.equal(
...@@ -106,15 +106,15 @@ describe('Drippie', () => { ...@@ -106,15 +106,15 @@ describe('Drippie', () => {
it('should allow setting between PAUSED and ACTIVE', async () => { it('should allow setting between PAUSED and ACTIVE', async () => {
await Drippie.create(DEFAULT_DRIP_NAME, DEFAULT_DRIP_CONFIG) await Drippie.create(DEFAULT_DRIP_NAME, DEFAULT_DRIP_CONFIG)
expect((await Drippie.drips(DEFAULT_DRIP_NAME)).status).to.equal(2) // PAUSED expect((await Drippie.drips(DEFAULT_DRIP_NAME)).status).to.equal(1) // PAUSED
await Drippie.status(DEFAULT_DRIP_NAME, 1) // ACTIVE await Drippie.status(DEFAULT_DRIP_NAME, 2) // ACTIVE
expect((await Drippie.drips(DEFAULT_DRIP_NAME)).status).to.equal(1) // ACTIVE expect((await Drippie.drips(DEFAULT_DRIP_NAME)).status).to.equal(2) // ACTIVE
await Drippie.status(DEFAULT_DRIP_NAME, 2) // PAUSED await Drippie.status(DEFAULT_DRIP_NAME, 1) // PAUSED
expect((await Drippie.drips(DEFAULT_DRIP_NAME)).status).to.equal(2) // PAUSED expect((await Drippie.drips(DEFAULT_DRIP_NAME)).status).to.equal(1) // PAUSED
}) })
it('should not allow setting status to NONE', async () => { it('should not allow setting status to NONE', async () => {
...@@ -128,8 +128,8 @@ describe('Drippie', () => { ...@@ -128,8 +128,8 @@ describe('Drippie', () => {
it('should not allow setting status to same status as before', async () => { it('should not allow setting status to same status as before', async () => {
await Drippie.create(DEFAULT_DRIP_NAME, DEFAULT_DRIP_CONFIG) await Drippie.create(DEFAULT_DRIP_NAME, DEFAULT_DRIP_CONFIG)
await expect(Drippie.status(DEFAULT_DRIP_NAME, 2)).to.be.revertedWith( await expect(Drippie.status(DEFAULT_DRIP_NAME, 1)).to.be.revertedWith(
'Drippie: cannot set drip status to same status as before' 'Drippie: cannot set drip status to the same status as its current status'
) )
}) })
...@@ -144,10 +144,10 @@ describe('Drippie', () => { ...@@ -144,10 +144,10 @@ describe('Drippie', () => {
it('should not allow setting status to ARCHIVED if ACTIVE', async () => { it('should not allow setting status to ARCHIVED if ACTIVE', async () => {
await Drippie.create(DEFAULT_DRIP_NAME, DEFAULT_DRIP_CONFIG) await Drippie.create(DEFAULT_DRIP_NAME, DEFAULT_DRIP_CONFIG)
await Drippie.status(DEFAULT_DRIP_NAME, 1) // ACTIVE await Drippie.status(DEFAULT_DRIP_NAME, 2) // ACTIVE
await expect(Drippie.status(DEFAULT_DRIP_NAME, 3)).to.be.revertedWith( await expect(Drippie.status(DEFAULT_DRIP_NAME, 3)).to.be.revertedWith(
'Drippie: drip must be paused to be archived' 'Drippie: drip must first be paused before being archived'
) )
}) })
...@@ -190,7 +190,7 @@ describe('Drippie', () => { ...@@ -190,7 +190,7 @@ describe('Drippie', () => {
describe('drip', () => { describe('drip', () => {
it('should drip the amount', async () => { it('should drip the amount', async () => {
await Drippie.create(DEFAULT_DRIP_NAME, DEFAULT_DRIP_CONFIG) await Drippie.create(DEFAULT_DRIP_NAME, DEFAULT_DRIP_CONFIG)
await Drippie.status(DEFAULT_DRIP_NAME, 1) // ACTIVE await Drippie.status(DEFAULT_DRIP_NAME, 2) // ACTIVE
await expect(Drippie.drip(DEFAULT_DRIP_NAME)).to.emit( await expect(Drippie.drip(DEFAULT_DRIP_NAME)).to.emit(
Drippie, Drippie,
...@@ -217,7 +217,7 @@ describe('Drippie', () => { ...@@ -217,7 +217,7 @@ describe('Drippie', () => {
], ],
}) })
await Drippie.status(DEFAULT_DRIP_NAME, 1) // ACTIVE await Drippie.status(DEFAULT_DRIP_NAME, 2) // ACTIVE
await Drippie.drip(DEFAULT_DRIP_NAME) await Drippie.drip(DEFAULT_DRIP_NAME)
expect(await SimpleStorage.get('0x' + '33'.repeat(32))).to.equal( expect(await SimpleStorage.get('0x' + '33'.repeat(32))).to.equal(
...@@ -248,7 +248,7 @@ describe('Drippie', () => { ...@@ -248,7 +248,7 @@ describe('Drippie', () => {
], ],
}) })
await Drippie.status(DEFAULT_DRIP_NAME, 1) // ACTIVE await Drippie.status(DEFAULT_DRIP_NAME, 2) // ACTIVE
await Drippie.drip(DEFAULT_DRIP_NAME) await Drippie.drip(DEFAULT_DRIP_NAME)
expect(await SimpleStorage.get('0x' + '33'.repeat(32))).to.equal( expect(await SimpleStorage.get('0x' + '33'.repeat(32))).to.equal(
...@@ -261,7 +261,7 @@ describe('Drippie', () => { ...@@ -261,7 +261,7 @@ describe('Drippie', () => {
it('should revert if dripping twice in one interval', async () => { it('should revert if dripping twice in one interval', async () => {
await Drippie.create(DEFAULT_DRIP_NAME, DEFAULT_DRIP_CONFIG) await Drippie.create(DEFAULT_DRIP_NAME, DEFAULT_DRIP_CONFIG)
await Drippie.status(DEFAULT_DRIP_NAME, 1) // ACTIVE await Drippie.status(DEFAULT_DRIP_NAME, 2) // ACTIVE
await Drippie.drip(DEFAULT_DRIP_NAME) await Drippie.drip(DEFAULT_DRIP_NAME)
await expect(Drippie.drip(DEFAULT_DRIP_NAME)).to.be.revertedWith( await expect(Drippie.drip(DEFAULT_DRIP_NAME)).to.be.revertedWith(
......
...@@ -136,10 +136,10 @@ export class DrippieMonService extends BaseServiceV2< ...@@ -136,10 +136,10 @@ export class DrippieMonService extends BaseServiceV2<
let executable: boolean let executable: boolean
try { try {
// To avoid making unnecessary RPC requests, filter out any drips that we don't expect to // To avoid making unnecessary RPC requests, filter out any drips that we don't expect to
// be executable right now. Only active drips (status = 1) and drips that are due to be // be executable right now. Only active drips (status = 2) and drips that are due to be
// executed are expected to be executable (but might not be based on the dripcheck). // executed are expected to be executable (but might not be based on the dripcheck).
if ( if (
drip.status === 1 && drip.status === 2 &&
drip.last.toNumber() + drip.config.interval.toNumber() < drip.last.toNumber() + drip.config.interval.toNumber() <
Date.now() / 1000 Date.now() / 1000
) { ) {
......
...@@ -3228,14 +3228,10 @@ ...@@ -3228,14 +3228,10 @@
dependencies: dependencies:
squirrelly "^8.0.8" squirrelly "^8.0.8"
"@rari-capital/solmate@https://github.com/rari-capital/solmate.git#8f9b23f8838670afda0fd8983f2c41e8037ae6bc": "@rari-capital/solmate@7.0.0-alpha.3", "@rari-capital/solmate@https://github.com/rari-capital/solmate.git#8f9b23f8838670afda0fd8983f2c41e8037ae6bc":
version "7.0.0-alpha.3" version "7.0.0-alpha.3"
resolved "https://github.com/rari-capital/solmate.git#8f9b23f8838670afda0fd8983f2c41e8037ae6bc" resolved "https://github.com/rari-capital/solmate.git#8f9b23f8838670afda0fd8983f2c41e8037ae6bc"
"@rari-capital/solmate@https://github.com/rari-capital/solmate.git#eaaccf88ac5290299884437e1aee098a96583d54":
version "6.4.0"
resolved "https://github.com/rari-capital/solmate.git#eaaccf88ac5290299884437e1aee098a96583d54"
"@resolver-engine/core@^0.3.3": "@resolver-engine/core@^0.3.3":
version "0.3.3" version "0.3.3"
resolved "https://registry.yarnpkg.com/@resolver-engine/core/-/core-0.3.3.tgz#590f77d85d45bc7ecc4e06c654f41345db6ca967" resolved "https://registry.yarnpkg.com/@resolver-engine/core/-/core-0.3.3.tgz#590f77d85d45bc7ecc4e06c654f41345db6ca967"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment