// TODO: this could result in a bad origin being selected if we are past the seq
ifpastSeqDrift{
// drift & should instead advance to the next origin.
returneth.L1BlockRef{},fmt.Errorf("cannot build next L2 block past current L1 origin %s by more than sequencer time drift, and failed to find next L1 origin: %w",currentOrigin,err)
log.Error("Failed to get next origin. Falling back to current origin","err",err)
}
iferrors.Is(err,ethereum.NotFound){
log.Debug("No next L1 block found, repeating current origin")
}else{
log.Error("Failed to get next origin. Falling back to current origin","err",err)
"metadata":"{\"compiler\":{\"version\":\"0.8.9+commit.e5eed63a\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"anonymous\":false,\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"threshold\",\"type\":\"uint256\"}],\"indexed\":false,\"internalType\":\"struct CheckBalanceHigh.Params\",\"name\":\"params\",\"type\":\"tuple\"}],\"name\":\"_EventToExposeStructInABI__Params\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"_params\",\"type\":\"bytes\"}],\"name\":\"check\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"title\":\"CheckBalanceHigh\",\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"notice\":\"DripCheck for checking if an account's balance is above a given threshold.\",\"version\":1}},\"settings\":{\"compilationTarget\":{\"contracts/universal/drippie/dripchecks/CheckBalanceHigh.sol\":\"CheckBalanceHigh\"},\"evmVersion\":\"london\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\",\"useLiteralContent\":true},\"optimizer\":{\"enabled\":true,\"runs\":10000},\"remappings\":[]},\"sources\":{\"contracts/universal/drippie/IDripCheck.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\npragma solidity ^0.8.9;\\n\\ninterface IDripCheck {\\n // DripCheck contracts that want to take parameters as inputs MUST expose a struct called\\n // Params and an event _EventForExposingParamsStructInABI(Params params). This makes it\\n // possible to easily encode parameters on the client side. Solidity does not support generics\\n // so it's not possible to do this with explicit typing.\\n\\n function check(bytes memory _params) external view returns (bool);\\n}\\n\",\"keccak256\":\"0x73db6ada63ed1f0eba24efd36099139e66908aa45c79c0d1defe2a59a9e9eb9d\",\"license\":\"MIT\"},\"contracts/universal/drippie/dripchecks/CheckBalanceHigh.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\npragma solidity ^0.8.9;\\n\\nimport { IDripCheck } from \\\"../IDripCheck.sol\\\";\\n\\n/**\\n * @title CheckBalanceHigh\\n * @notice DripCheck for checking if an account's balance is above a given threshold.\\n */\\ncontract CheckBalanceHigh is IDripCheck {\\n event _EventToExposeStructInABI__Params(Params params);\\n struct Params {\\n address target;\\n uint256 threshold;\\n }\\n\\n function check(bytes memory _params) external view returns (bool) {\\n Params memory params = abi.decode(_params, (Params));\\n\\n // Check target balance is above threshold.\\n return params.target.balance > params.threshold;\\n }\\n}\\n\",\"keccak256\":\"0x517f69b75b62b7b74d3d4fceb45ecacb9cf1d68b62a8517e2985a6e0da0a7575\",\"license\":\"MIT\"}},\"version\":1}",
"metadata":"{\"compiler\":{\"version\":\"0.8.16+commit.07a7930e\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"anonymous\":false,\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"threshold\",\"type\":\"uint256\"}],\"indexed\":false,\"internalType\":\"struct CheckBalanceHigh.Params\",\"name\":\"params\",\"type\":\"tuple\"}],\"name\":\"_EventToExposeStructInABI__Params\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"_params\",\"type\":\"bytes\"}],\"name\":\"check\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"}],\"devdoc\":{\"events\":{\"_EventToExposeStructInABI__Params((address,uint256))\":{\"params\":{\"params\":\"Parameters to encode.\"}}},\"kind\":\"dev\",\"methods\":{\"check(bytes)\":{\"params\":{\"_params\":\"Encoded parameters for the drip check.\"},\"returns\":{\"_0\":\"Whether the drip should be executed.\"}}},\"title\":\"CheckBalanceHigh\",\"version\":1},\"userdoc\":{\"events\":{\"_EventToExposeStructInABI__Params((address,uint256))\":{\"notice\":\"External event used to help client-side tooling encode parameters.\"}},\"kind\":\"user\",\"methods\":{\"check(bytes)\":{\"notice\":\"Checks whether a drip should be executable.\"}},\"notice\":\"DripCheck for checking if an account's balance is above a given threshold.\",\"version\":1}},\"settings\":{\"compilationTarget\":{\"contracts/universal/drippie/dripchecks/CheckBalanceHigh.sol\":\"CheckBalanceHigh\"},\"evmVersion\":\"london\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\",\"useLiteralContent\":true},\"optimizer\":{\"enabled\":true,\"runs\":10000},\"remappings\":[]},\"sources\":{\"contracts/universal/drippie/IDripCheck.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\npragma solidity ^0.8.0;\\n\\ninterface IDripCheck {\\n // DripCheck contracts that want to take parameters as inputs MUST expose a struct called\\n // Params and an event _EventForExposingParamsStructInABI(Params params). This makes it\\n // possible to easily encode parameters on the client side. Solidity does not support generics\\n // so it's not possible to do this with explicit typing.\\n\\n /**\\n * @notice Checks whether a drip should be executable.\\n *\\n * @param _params Encoded parameters for the drip check.\\n *\\n * @return Whether the drip should be executed.\\n */\\n function check(bytes memory _params) external view returns (bool);\\n}\\n\",\"keccak256\":\"0xb52c89360566b2963dfd82cb2cc23f0c3ce4503a69e8563878e8aa80b6c60b3f\",\"license\":\"MIT\"},\"contracts/universal/drippie/dripchecks/CheckBalanceHigh.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\npragma solidity 0.8.16;\\n\\nimport { IDripCheck } from \\\"../IDripCheck.sol\\\";\\n\\n/**\\n * @title CheckBalanceHigh\\n * @notice DripCheck for checking if an account's balance is above a given threshold.\\n */\\ncontract CheckBalanceHigh is IDripCheck {\\n struct Params {\\n address target;\\n uint256 threshold;\\n }\\n\\n /**\\n * @notice External event used to help client-side tooling encode parameters.\\n *\\n * @param params Parameters to encode.\\n */\\n event _EventToExposeStructInABI__Params(Params params);\\n\\n /**\\n * @inheritdoc IDripCheck\\n */\\n function check(bytes memory _params) external view returns (bool) {\\n Params memory params = abi.decode(_params, (Params));\\n\\n // Check target balance is above threshold.\\n return params.target.balance > params.threshold;\\n }\\n}\\n\",\"keccak256\":\"0xcb27d9e50a7c32406872b8fdc4ca62ee0d27372eb9077657f6d16f3cd3b58c85\",\"license\":\"MIT\"}},\"version\":1}",
"metadata":"{\"compiler\":{\"version\":\"0.8.9+commit.e5eed63a\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"anonymous\":false,\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"threshold\",\"type\":\"uint256\"}],\"indexed\":false,\"internalType\":\"struct CheckBalanceLow.Params\",\"name\":\"params\",\"type\":\"tuple\"}],\"name\":\"_EventToExposeStructInABI__Params\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"_params\",\"type\":\"bytes\"}],\"name\":\"check\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"title\":\"CheckBalanceLow\",\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"notice\":\"DripCheck for checking if an account's balance is below a given threshold.\",\"version\":1}},\"settings\":{\"compilationTarget\":{\"contracts/universal/drippie/dripchecks/CheckBalanceLow.sol\":\"CheckBalanceLow\"},\"evmVersion\":\"london\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\",\"useLiteralContent\":true},\"optimizer\":{\"enabled\":true,\"runs\":10000},\"remappings\":[]},\"sources\":{\"contracts/universal/drippie/IDripCheck.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\npragma solidity ^0.8.9;\\n\\ninterface IDripCheck {\\n // DripCheck contracts that want to take parameters as inputs MUST expose a struct called\\n // Params and an event _EventForExposingParamsStructInABI(Params params). This makes it\\n // possible to easily encode parameters on the client side. Solidity does not support generics\\n // so it's not possible to do this with explicit typing.\\n\\n function check(bytes memory _params) external view returns (bool);\\n}\\n\",\"keccak256\":\"0x73db6ada63ed1f0eba24efd36099139e66908aa45c79c0d1defe2a59a9e9eb9d\",\"license\":\"MIT\"},\"contracts/universal/drippie/dripchecks/CheckBalanceLow.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\npragma solidity ^0.8.9;\\n\\nimport { IDripCheck } from \\\"../IDripCheck.sol\\\";\\n\\n/**\\n * @title CheckBalanceLow\\n * @notice DripCheck for checking if an account's balance is below a given threshold.\\n */\\ncontract CheckBalanceLow is IDripCheck {\\n event _EventToExposeStructInABI__Params(Params params);\\n struct Params {\\n address target;\\n uint256 threshold;\\n }\\n\\n function check(bytes memory _params) external view returns (bool) {\\n Params memory params = abi.decode(_params, (Params));\\n\\n // Check target ETH balance is below threshold.\\n return params.target.balance < params.threshold;\\n }\\n}\\n\",\"keccak256\":\"0xd6d8e6abcc1428666132e9d8140243fff23c564b6474927e18f30fe078801842\",\"license\":\"MIT\"}},\"version\":1}",
"metadata":"{\"compiler\":{\"version\":\"0.8.16+commit.07a7930e\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"anonymous\":false,\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"threshold\",\"type\":\"uint256\"}],\"indexed\":false,\"internalType\":\"struct CheckBalanceLow.Params\",\"name\":\"params\",\"type\":\"tuple\"}],\"name\":\"_EventToExposeStructInABI__Params\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"_params\",\"type\":\"bytes\"}],\"name\":\"check\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"}],\"devdoc\":{\"events\":{\"_EventToExposeStructInABI__Params((address,uint256))\":{\"params\":{\"params\":\"Parameters to encode.\"}}},\"kind\":\"dev\",\"methods\":{\"check(bytes)\":{\"params\":{\"_params\":\"Encoded parameters for the drip check.\"},\"returns\":{\"_0\":\"Whether the drip should be executed.\"}}},\"title\":\"CheckBalanceLow\",\"version\":1},\"userdoc\":{\"events\":{\"_EventToExposeStructInABI__Params((address,uint256))\":{\"notice\":\"External event used to help client-side tooling encode parameters.\"}},\"kind\":\"user\",\"methods\":{\"check(bytes)\":{\"notice\":\"Checks whether a drip should be executable.\"}},\"notice\":\"DripCheck for checking if an account's balance is below a given threshold.\",\"version\":1}},\"settings\":{\"compilationTarget\":{\"contracts/universal/drippie/dripchecks/CheckBalanceLow.sol\":\"CheckBalanceLow\"},\"evmVersion\":\"london\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\",\"useLiteralContent\":true},\"optimizer\":{\"enabled\":true,\"runs\":10000},\"remappings\":[]},\"sources\":{\"contracts/universal/drippie/IDripCheck.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\npragma solidity ^0.8.0;\\n\\ninterface IDripCheck {\\n // DripCheck contracts that want to take parameters as inputs MUST expose a struct called\\n // Params and an event _EventForExposingParamsStructInABI(Params params). This makes it\\n // possible to easily encode parameters on the client side. Solidity does not support generics\\n // so it's not possible to do this with explicit typing.\\n\\n /**\\n * @notice Checks whether a drip should be executable.\\n *\\n * @param _params Encoded parameters for the drip check.\\n *\\n * @return Whether the drip should be executed.\\n */\\n function check(bytes memory _params) external view returns (bool);\\n}\\n\",\"keccak256\":\"0xb52c89360566b2963dfd82cb2cc23f0c3ce4503a69e8563878e8aa80b6c60b3f\",\"license\":\"MIT\"},\"contracts/universal/drippie/dripchecks/CheckBalanceLow.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\npragma solidity 0.8.16;\\n\\nimport { IDripCheck } from \\\"../IDripCheck.sol\\\";\\n\\n/**\\n * @title CheckBalanceLow\\n * @notice DripCheck for checking if an account's balance is below a given threshold.\\n */\\ncontract CheckBalanceLow is IDripCheck {\\n struct Params {\\n address target;\\n uint256 threshold;\\n }\\n\\n /**\\n * @notice External event used to help client-side tooling encode parameters.\\n *\\n * @param params Parameters to encode.\\n */\\n event _EventToExposeStructInABI__Params(Params params);\\n\\n /**\\n * @inheritdoc IDripCheck\\n */\\n function check(bytes memory _params) external view returns (bool) {\\n Params memory params = abi.decode(_params, (Params));\\n\\n // Check target ETH balance is below threshold.\\n return params.target.balance < params.threshold;\\n }\\n}\\n\",\"keccak256\":\"0x6a1187a80093770931296d3360b1ecc7d17f69157fe88f628989b257548b564b\",\"license\":\"MIT\"}},\"version\":1}",
"metadata":"{\"compiler\":{\"version\":\"0.8.9+commit.e5eed63a\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"anonymous\":false,\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"treasury\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"threshold\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"}],\"indexed\":false,\"internalType\":\"struct CheckGelatoLow.Params\",\"name\":\"params\",\"type\":\"tuple\"}],\"name\":\"_EventToExposeStructInABI__Params\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"_params\",\"type\":\"bytes\"}],\"name\":\"check\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"title\":\"CheckGelatoLow\",\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"notice\":\"DripCheck for checking if an account's Gelato ETH balance is below some threshold.\",\"version\":1}},\"settings\":{\"compilationTarget\":{\"contracts/universal/drippie/dripchecks/CheckGelatoLow.sol\":\"CheckGelatoLow\"},\"evmVersion\":\"london\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\",\"useLiteralContent\":true},\"optimizer\":{\"enabled\":true,\"runs\":10000},\"remappings\":[]},\"sources\":{\"contracts/universal/drippie/IDripCheck.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\npragma solidity ^0.8.9;\\n\\ninterface IDripCheck {\\n // DripCheck contracts that want to take parameters as inputs MUST expose a struct called\\n // Params and an event _EventForExposingParamsStructInABI(Params params). This makes it\\n // possible to easily encode parameters on the client side. Solidity does not support generics\\n // so it's not possible to do this with explicit typing.\\n\\n function check(bytes memory _params) external view returns (bool);\\n}\\n\",\"keccak256\":\"0x73db6ada63ed1f0eba24efd36099139e66908aa45c79c0d1defe2a59a9e9eb9d\",\"license\":\"MIT\"},\"contracts/universal/drippie/dripchecks/CheckGelatoLow.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\npragma solidity ^0.8.9;\\n\\nimport { IDripCheck } from \\\"../IDripCheck.sol\\\";\\n\\ninterface IGelatoTreasury {\\n function userTokenBalance(address _user, address _token) external view returns (uint256);\\n}\\n\\n/**\\n * @title CheckGelatoLow\\n * @notice DripCheck for checking if an account's Gelato ETH balance is below some threshold.\\n */\\ncontract CheckGelatoLow is IDripCheck {\\n event _EventToExposeStructInABI__Params(Params params);\\n struct Params {\\n address treasury;\\n uint256 threshold;\\n address recipient;\\n }\\n\\n function check(bytes memory _params) external view returns (bool) {\\n Params memory params = abi.decode(_params, (Params));\\n\\n // Check GelatoTreasury ETH balance is below threshold.\\n return\\n IGelatoTreasury(params.treasury).userTokenBalance(\\n params.recipient,\\n // Gelato represents ETH as 0xeeeee....eeeee\\n 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE\\n ) < params.threshold;\\n }\\n}\\n\",\"keccak256\":\"0x8d936bc5e037a1b05225f2cd208ef6899b0d29cd3b91c73e5c16762eb242e5ef\",\"license\":\"MIT\"}},\"version\":1}",
"metadata":"{\"compiler\":{\"version\":\"0.8.16+commit.07a7930e\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"anonymous\":false,\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"treasury\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"threshold\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"}],\"indexed\":false,\"internalType\":\"struct CheckGelatoLow.Params\",\"name\":\"params\",\"type\":\"tuple\"}],\"name\":\"_EventToExposeStructInABI__Params\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"_params\",\"type\":\"bytes\"}],\"name\":\"check\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"}],\"devdoc\":{\"events\":{\"_EventToExposeStructInABI__Params((address,uint256,address))\":{\"params\":{\"params\":\"Parameters to encode.\"}}},\"kind\":\"dev\",\"methods\":{\"check(bytes)\":{\"params\":{\"_params\":\"Encoded parameters for the drip check.\"},\"returns\":{\"_0\":\"Whether the drip should be executed.\"}}},\"title\":\"CheckGelatoLow\",\"version\":1},\"userdoc\":{\"events\":{\"_EventToExposeStructInABI__Params((address,uint256,address))\":{\"notice\":\"External event used to help client-side tooling encode parameters.\"}},\"kind\":\"user\",\"methods\":{\"check(bytes)\":{\"notice\":\"Checks whether a drip should be executable.\"}},\"notice\":\"DripCheck for checking if an account's Gelato ETH balance is below some threshold.\",\"version\":1}},\"settings\":{\"compilationTarget\":{\"contracts/universal/drippie/dripchecks/CheckGelatoLow.sol\":\"CheckGelatoLow\"},\"evmVersion\":\"london\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\",\"useLiteralContent\":true},\"optimizer\":{\"enabled\":true,\"runs\":10000},\"remappings\":[]},\"sources\":{\"contracts/universal/drippie/IDripCheck.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\npragma solidity ^0.8.0;\\n\\ninterface IDripCheck {\\n // DripCheck contracts that want to take parameters as inputs MUST expose a struct called\\n // Params and an event _EventForExposingParamsStructInABI(Params params). This makes it\\n // possible to easily encode parameters on the client side. Solidity does not support generics\\n // so it's not possible to do this with explicit typing.\\n\\n /**\\n * @notice Checks whether a drip should be executable.\\n *\\n * @param _params Encoded parameters for the drip check.\\n *\\n * @return Whether the drip should be executed.\\n */\\n function check(bytes memory _params) external view returns (bool);\\n}\\n\",\"keccak256\":\"0xb52c89360566b2963dfd82cb2cc23f0c3ce4503a69e8563878e8aa80b6c60b3f\",\"license\":\"MIT\"},\"contracts/universal/drippie/dripchecks/CheckGelatoLow.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\npragma solidity 0.8.16;\\n\\nimport { IDripCheck } from \\\"../IDripCheck.sol\\\";\\n\\ninterface IGelatoTreasury {\\n function userTokenBalance(address _user, address _token) external view returns (uint256);\\n}\\n\\n/**\\n * @title CheckGelatoLow\\n * @notice DripCheck for checking if an account's Gelato ETH balance is below some threshold.\\n */\\ncontract CheckGelatoLow is IDripCheck {\\n struct Params {\\n address treasury;\\n uint256 threshold;\\n address recipient;\\n }\\n\\n /**\\n * @notice External event used to help client-side tooling encode parameters.\\n *\\n * @param params Parameters to encode.\\n */\\n event _EventToExposeStructInABI__Params(Params params);\\n\\n /**\\n * @inheritdoc IDripCheck\\n */\\n function check(bytes memory _params) external view returns (bool) {\\n Params memory params = abi.decode(_params, (Params));\\n\\n // Check GelatoTreasury ETH balance is below threshold.\\n return\\n IGelatoTreasury(params.treasury).userTokenBalance(\\n params.recipient,\\n // Gelato represents ETH as 0xeeeee....eeeee\\n 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE\\n ) < params.threshold;\\n }\\n}\\n\",\"keccak256\":\"0x1a127a2c8955525cc2e5cd3c6703edc785c662a79222f524df4574cf47ddd864\",\"license\":\"MIT\"}},\"version\":1}",
"metadata":"{\"compiler\":{\"version\":\"0.8.9+commit.e5eed63a\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"name\":\"check\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"pure\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"title\":\"CheckTrue\",\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"notice\":\"DripCheck that always returns true.\",\"version\":1}},\"settings\":{\"compilationTarget\":{\"contracts/universal/drippie/dripchecks/CheckTrue.sol\":\"CheckTrue\"},\"evmVersion\":\"london\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\",\"useLiteralContent\":true},\"optimizer\":{\"enabled\":true,\"runs\":10000},\"remappings\":[]},\"sources\":{\"contracts/universal/drippie/IDripCheck.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\npragma solidity ^0.8.9;\\n\\ninterface IDripCheck {\\n // DripCheck contracts that want to take parameters as inputs MUST expose a struct called\\n // Params and an event _EventForExposingParamsStructInABI(Params params). This makes it\\n // possible to easily encode parameters on the client side. Solidity does not support generics\\n // so it's not possible to do this with explicit typing.\\n\\n function check(bytes memory _params) external view returns (bool);\\n}\\n\",\"keccak256\":\"0x73db6ada63ed1f0eba24efd36099139e66908aa45c79c0d1defe2a59a9e9eb9d\",\"license\":\"MIT\"},\"contracts/universal/drippie/dripchecks/CheckTrue.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\npragma solidity ^0.8.9;\\n\\nimport { IDripCheck } from \\\"../IDripCheck.sol\\\";\\n\\n/**\\n * @title CheckTrue\\n * @notice DripCheck that always returns true.\\n */\\ncontract CheckTrue is IDripCheck {\\n function check(bytes memory) external pure returns (bool) {\\n return true;\\n }\\n}\\n\",\"keccak256\":\"0x2ed60821a4302130b567da45f15dce5a7c41e5d5b016c32ec09c92d4fb5ba6e6\",\"license\":\"MIT\"}},\"version\":1}",
"metadata":"{\"compiler\":{\"version\":\"0.8.16+commit.07a7930e\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"name\":\"check\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"pure\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{\"check(bytes)\":{\"params\":{\"_params\":\"Encoded parameters for the drip check.\"},\"returns\":{\"_0\":\"Whether the drip should be executed.\"}}},\"title\":\"CheckTrue\",\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{\"check(bytes)\":{\"notice\":\"Checks whether a drip should be executable.\"}},\"notice\":\"DripCheck that always returns true.\",\"version\":1}},\"settings\":{\"compilationTarget\":{\"contracts/universal/drippie/dripchecks/CheckTrue.sol\":\"CheckTrue\"},\"evmVersion\":\"london\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\",\"useLiteralContent\":true},\"optimizer\":{\"enabled\":true,\"runs\":10000},\"remappings\":[]},\"sources\":{\"contracts/universal/drippie/IDripCheck.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\npragma solidity ^0.8.0;\\n\\ninterface IDripCheck {\\n // DripCheck contracts that want to take parameters as inputs MUST expose a struct called\\n // Params and an event _EventForExposingParamsStructInABI(Params params). This makes it\\n // possible to easily encode parameters on the client side. Solidity does not support generics\\n // so it's not possible to do this with explicit typing.\\n\\n /**\\n * @notice Checks whether a drip should be executable.\\n *\\n * @param _params Encoded parameters for the drip check.\\n *\\n * @return Whether the drip should be executed.\\n */\\n function check(bytes memory _params) external view returns (bool);\\n}\\n\",\"keccak256\":\"0xb52c89360566b2963dfd82cb2cc23f0c3ce4503a69e8563878e8aa80b6c60b3f\",\"license\":\"MIT\"},\"contracts/universal/drippie/dripchecks/CheckTrue.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\npragma solidity 0.8.16;\\n\\nimport { IDripCheck } from \\\"../IDripCheck.sol\\\";\\n\\n/**\\n * @title CheckTrue\\n * @notice DripCheck that always returns true.\\n */\\ncontract CheckTrue is IDripCheck {\\n /**\\n * @inheritdoc IDripCheck\\n */\\n function check(bytes memory) external pure returns (bool) {\\n return true;\\n }\\n}\\n\",\"keccak256\":\"0xf2f5474f12983c30ca4fe9d19e7f88e6d2262e4a6f779e86b4a2117498fdbea5\",\"license\":\"MIT\"}},\"version\":1}",
"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport { ERC20 } from \"@rari-capital/solmate/src/tokens/ERC20.sol\";\n\ncontract TestERC20 is ERC20 {\n constructor() ERC20(\"TEST\", \"TST\", 18) {}\n\n function mint(address to, uint256 value) public {\n _mint(to, value);\n }\n}\n"
},
"@rari-capital/solmate/src/tokens/ERC20.sol":{
"content":"// SPDX-License-Identifier: MIT\npragma solidity >=0.8.0;\n\n/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.\n/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC20.sol)\n/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)\n/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.\nabstract contract ERC20 {\n /*//////////////////////////////////////////////////////////////\n EVENTS\n //////////////////////////////////////////////////////////////*/\n\n event Transfer(address indexed from, address indexed to, uint256 amount);\n\n event Approval(address indexed owner, address indexed spender, uint256 amount);\n\n /*//////////////////////////////////////////////////////////////\n METADATA STORAGE\n //////////////////////////////////////////////////////////////*/\n\n string public name;\n\n string public symbol;\n\n uint8 public immutable decimals;\n\n /*//////////////////////////////////////////////////////////////\n ERC20 STORAGE\n //////////////////////////////////////////////////////////////*/\n\n uint256 public totalSupply;\n\n mapping(address => uint256) public balanceOf;\n\n mapping(address => mapping(address => uint256)) public allowance;\n\n /*//////////////////////////////////////////////////////////////\n EIP-2612 STORAGE\n //////////////////////////////////////////////////////////////*/\n\n uint256 internal immutable INITIAL_CHAIN_ID;\n\n bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;\n\n mapping(address => uint256) public nonces;\n\n /*//////////////////////////////////////////////////////////////\n CONSTRUCTOR\n //////////////////////////////////////////////////////////////*/\n\n constructor(\n string memory _name,\n string memory _symbol,\n uint8 _decimals\n ) {\n name = _name;\n symbol = _symbol;\n decimals = _decimals;\n\n INITIAL_CHAIN_ID = block.chainid;\n INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();\n }\n\n /*//////////////////////////////////////////////////////////////\n ERC20 LOGIC\n //////////////////////////////////////////////////////////////*/\n\n function approve(address spender, uint256 amount) public virtual returns (bool) {\n allowance[msg.sender][spender] = amount;\n\n emit Approval(msg.sender, spender, amount);\n\n return true;\n }\n\n function transfer(address to, uint256 amount) public virtual returns (bool) {\n balanceOf[msg.sender] -= amount;\n\n // Cannot overflow because the sum of all user\n // balances can't exceed the max uint256 value.\n unchecked {\n balanceOf[to] += amount;\n }\n\n emit Transfer(msg.sender, to, amount);\n\n return true;\n }\n\n function transferFrom(\n address from,\n address to,\n uint256 amount\n ) public virtual returns (bool) {\n uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.\n\n if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;\n\n balanceOf[from] -= amount;\n\n // Cannot overflow because the sum of all user\n // balances can't exceed the max uint256 value.\n unchecked {\n balanceOf[to] += amount;\n }\n\n emit Transfer(from, to, amount);\n\n return true;\n }\n\n /*//////////////////////////////////////////////////////////////\n EIP-2612 LOGIC\n //////////////////////////////////////////////////////////////*/\n\n function permit(\n address owner,\n address spender,\n uint256 value,\n uint256 deadline,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) public virtual {\n require(deadline >= block.timestamp, \"PERMIT_DEADLINE_EXPIRED\");\n\n // Unchecked because the only math done is incrementing\n // the owner's nonce which cannot realistically overflow.\n unchecked {\n address recoveredAddress = ecrecover(\n keccak256(\n abi.encodePacked(\n\"\\x19\\x01\",\n DOMAIN_SEPARATOR(),\n keccak256(\n abi.encode(\n keccak256(\n\"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)\"\n ),\n owner,\n spender,\n value,\n nonces[owner]++,\n deadline\n )\n )\n )\n ),\n v,\n r,\n s\n );\n\n require(recoveredAddress != address(0) && recoveredAddress == owner, \"INVALID_SIGNER\");\n\n allowance[recoveredAddress][spender] = value;\n }\n\n emit Approval(owner, spender, value);\n }\n\n function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {\n return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();\n }\n\n function computeDomainSeparator() internal view virtual returns (bytes32) {\n return\n keccak256(\n abi.encode(\n keccak256(\"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)\"),\n keccak256(bytes(name)),\n keccak256(\"1\"),\n block.chainid,\n address(this)\n )\n );\n }\n\n /*//////////////////////////////////////////////////////////////\n INTERNAL MINT/BURN LOGIC\n //////////////////////////////////////////////////////////////*/\n\n function _mint(address to, uint256 amount) internal virtual {\n totalSupply += amount;\n\n // Cannot overflow because the sum of all user\n // balances can't exceed the max uint256 value.\n unchecked {\n balanceOf[to] += amount;\n }\n\n emit Transfer(address(0), to, amount);\n }\n\n function _burn(address from, uint256 amount) internal virtual {\n balanceOf[from] -= amount;\n\n // Cannot underflow because a user's balance\n // will never be larger than the total supply.\n unchecked {\n totalSupply -= amount;\n }\n\n emit Transfer(from, address(0), amount);\n }\n}\n"
},
"contracts/universal/AssetReceiver.sol":{
"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport { ERC20 } from \"@rari-capital/solmate/src/tokens/ERC20.sol\";\nimport { ERC721 } from \"@rari-capital/solmate/src/tokens/ERC721.sol\";\nimport { Transactor } from \"./Transactor.sol\";\n\n/**\n * @title AssetReceiver\n * @notice AssetReceiver is a minimal contract for receiving funds assets in the form of either\n * ETH, ERC20 tokens, or ERC721 tokens. Only the contract owner may withdraw the assets.\n */\ncontract AssetReceiver is Transactor {\n /**\n * @notice Emitted when ETH is received by this address.\n *\n * @param from Address that sent ETH to this contract.\n * @param amount Amount of ETH received.\n */\n event ReceivedETH(address indexed from, uint256 amount);\n\n /**\n * @notice Emitted when ETH is withdrawn from this address.\n *\n * @param withdrawer Address that triggered the withdrawal.\n * @param recipient Address that received the withdrawal.\n * @param amount ETH amount withdrawn.\n */\n event WithdrewETH(address indexed withdrawer, address indexed recipient, uint256 amount);\n\n /**\n * @notice Emitted when ERC20 tokens are withdrawn from this address.\n *\n * @param withdrawer Address that triggered the withdrawal.\n * @param recipient Address that received the withdrawal.\n * @param asset Address of the token being withdrawn.\n * @param amount ERC20 amount withdrawn.\n */\n event WithdrewERC20(\n address indexed withdrawer,\n address indexed recipient,\n address indexed asset,\n uint256 amount\n );\n\n /**\n * @notice Emitted when ERC20 tokens are withdrawn from this address.\n *\n * @param withdrawer Address that triggered the withdrawal.\n * @param recipient Address that received the withdrawal.\n * @param asset Address of the token being withdrawn.\n * @param id Token ID being withdrawn.\n */\n event WithdrewERC721(\n address indexed withdrawer,\n address indexed recipient,\n address indexed asset,\n uint256 id\n );\n\n /**\n * @param _owner Initial contract owner.\n */\n constructor(address _owner) Transactor(_owner) {}\n\n /**\n * @notice Make sure we can receive ETH.\n */\n receive() external payable {\n emit ReceivedETH(msg.sender, msg.value);\n }\n\n /**\n * @notice Withdraws full ETH balance to the recipient.\n *\n * @param _to Address to receive the ETH balance.\n */\n function withdrawETH(address payable _to) external onlyOwner {\n withdrawETH(_to, address(this).balance);\n }\n\n /**\n * @notice Withdraws partial ETH balance to the recipient.\n *\n * @param _to Address to receive the ETH balance.\n * @param _amount Amount of ETH to withdraw.\n */\n function withdrawETH(address payable _to, uint256 _amount) public onlyOwner {\n // slither-disable-next-line reentrancy-unlimited-gas\n (bool success, ) = _to.call{ value: _amount }(\"\");\n emit WithdrewETH(msg.sender, _to, _amount);\n }\n\n /**\n * @notice Withdraws full ERC20 balance to the recipient.\n *\n * @param _asset ERC20 token to withdraw.\n * @param _to Address to receive the ERC20 balance.\n */\n function withdrawERC20(ERC20 _asset, address _to) external onlyOwner {\n withdrawERC20(_asset, _to, _asset.balanceOf(address(this)));\n }\n\n /**\n * @notice Withdraws partial ERC20 balance to the recipient.\n *\n * @param _asset ERC20 token to withdraw.\n * @param _to Address to receive the ERC20 balance.\n * @param _amount Amount of ERC20 to withdraw.\n */\n function withdrawERC20(\n ERC20 _asset,\n address _to,\n uint256 _amount\n ) public onlyOwner {\n // slither-disable-next-line unchecked-transfer\n _asset.transfer(_to, _amount);\n // slither-disable-next-line reentrancy-events\n emit WithdrewERC20(msg.sender, _to, address(_asset), _amount);\n }\n\n /**\n * @notice Withdraws ERC721 token to the recipient.\n *\n * @param _asset ERC721 token to withdraw.\n * @param _to Address to receive the ERC721 token.\n * @param _id Token ID of the ERC721 token to withdraw.\n */\n function withdrawERC721(\n ERC721 _asset,\n address _to,\n uint256 _id\n ) external onlyOwner {\n _asset.transferFrom(address(this), _to, _id);\n // slither-disable-next-line reentrancy-events\n emit WithdrewERC721(msg.sender, _to, address(_asset), _id);\n }\n}\n"
},
"@rari-capital/solmate/src/tokens/ERC721.sol":{
"content":"// SPDX-License-Identifier: MIT\npragma solidity >=0.8.0;\n\n/// @notice Modern, minimalist, and gas efficient ERC-721 implementation.\n/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC721.sol)\nabstract contract ERC721 {\n /*//////////////////////////////////////////////////////////////\n EVENTS\n //////////////////////////////////////////////////////////////*/\n\n event Transfer(address indexed from, address indexed to, uint256 indexed id);\n\n event Approval(address indexed owner, address indexed spender, uint256 indexed id);\n\n event ApprovalForAll(address indexed owner, address indexed operator, bool approved);\n\n /*//////////////////////////////////////////////////////////////\n METADATA STORAGE/LOGIC\n //////////////////////////////////////////////////////////////*/\n\n string public name;\n\n string public symbol;\n\n function tokenURI(uint256 id) public view virtual returns (string memory);\n\n /*//////////////////////////////////////////////////////////////\n ERC721 BALANCE/OWNER STORAGE\n //////////////////////////////////////////////////////////////*/\n\n mapping(uint256 => address) internal _ownerOf;\n\n mapping(address => uint256) internal _balanceOf;\n\n function ownerOf(uint256 id) public view virtual returns (address owner) {\n require((owner = _ownerOf[id]) != address(0), \"NOT_MINTED\");\n }\n\n function balanceOf(address owner) public view virtual returns (uint256) {\n require(owner != address(0), \"ZERO_ADDRESS\");\n\n return _balanceOf[owner];\n }\n\n /*//////////////////////////////////////////////////////////////\n ERC721 APPROVAL STORAGE\n //////////////////////////////////////////////////////////////*/\n\n mapping(uint256 => address) public getApproved;\n\n mapping(address => mapping(address => bool)) public isApprovedForAll;\n\n /*//////////////////////////////////////////////////////////////\n CONSTRUCTOR\n //////////////////////////////////////////////////////////////*/\n\n constructor(string memory _name, string memory _symbol) {\n name = _name;\n symbol = _symbol;\n }\n\n /*//////////////////////////////////////////////////////////////\n ERC721 LOGIC\n //////////////////////////////////////////////////////////////*/\n\n function approve(address spender, uint256 id) public virtual {\n address owner = _ownerOf[id];\n\n require(msg.sender == owner || isApprovedForAll[owner][msg.sender], \"NOT_AUTHORIZED\");\n\n getApproved[id] = spender;\n\n emit Approval(owner, spender, id);\n }\n\n function setApprovalForAll(address operator, bool approved) public virtual {\n isApprovedForAll[msg.sender][operator] = approved;\n\n emit ApprovalForAll(msg.sender, operator, approved);\n }\n\n function transferFrom(\n address from,\n address to,\n uint256 id\n ) public virtual {\n require(from == _ownerOf[id], \"WRONG_FROM\");\n\n require(to != address(0), \"INVALID_RECIPIENT\");\n\n require(\n msg.sender == from || isApprovedForAll[from][msg.sender] || msg.sender == getApproved[id],\n\"NOT_AUTHORIZED\"\n );\n\n // Underflow of the sender's balance is impossible because we check for\n // ownership above and the recipient's balance can't realistically overflow.\n unchecked {\n _balanceOf[from]--;\n\n _balanceOf[to]++;\n }\n\n _ownerOf[id] = to;\n\n delete getApproved[id];\n\n emit Transfer(from, to, id);\n }\n\n function safeTransferFrom(\n address from,\n address to,\n uint256 id\n ) public virtual {\n transferFrom(from, to, id);\n\n if (to.code.length != 0)\n require(\n ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, \"\") ==\n ERC721TokenReceiver.onERC721Received.selector,\n\"UNSAFE_RECIPIENT\"\n );\n }\n\n function safeTransferFrom(\n address from,\n address to,\n uint256 id,\n bytes calldata data\n ) public virtual {\n transferFrom(from, to, id);\n\n if (to.code.length != 0)\n require(\n ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, data) ==\n ERC721TokenReceiver.onERC721Received.selector,\n\"UNSAFE_RECIPIENT\"\n );\n }\n\n /*//////////////////////////////////////////////////////////////\n ERC165 LOGIC\n //////////////////////////////////////////////////////////////*/\n\n function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {\n return\n interfaceId == 0x01ffc9a7 || // ERC165 Interface ID for ERC165\n interfaceId == 0x80ac58cd || // ERC165 Interface ID for ERC721\n interfaceId == 0x5b5e139f; // ERC165 Interface ID for ERC721Metadata\n }\n\n /*//////////////////////////////////////////////////////////////\n INTERNAL MINT/BURN LOGIC\n //////////////////////////////////////////////////////////////*/\n\n function _mint(address to, uint256 id) internal virtual {\n require(to != address(0), \"INVALID_RECIPIENT\");\n\n require(_ownerOf[id] == address(0), \"ALREADY_MINTED\");\n\n // Counter overflow is incredibly unrealistic.\n unchecked {\n _balanceOf[to]++;\n }\n\n _ownerOf[id] = to;\n\n emit Transfer(address(0), to, id);\n }\n\n function _burn(uint256 id) internal virtual {\n address owner = _ownerOf[id];\n\n require(owner != address(0), \"NOT_MINTED\");\n\n // Ownership check above ensures no underflow.\n unchecked {\n _balanceOf[owner]--;\n }\n\n delete _ownerOf[id];\n\n delete getApproved[id];\n\n emit Transfer(owner, address(0), id);\n }\n\n /*//////////////////////////////////////////////////////////////\n INTERNAL SAFE MINT LOGIC\n //////////////////////////////////////////////////////////////*/\n\n function _safeMint(address to, uint256 id) internal virtual {\n _mint(to, id);\n\n if (to.code.length != 0)\n require(\n ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, \"\") ==\n ERC721TokenReceiver.onERC721Received.selector,\n\"UNSAFE_RECIPIENT\"\n );\n }\n\n function _safeMint(\n address to,\n uint256 id,\n bytes memory data\n ) internal virtual {\n _mint(to, id);\n\n if (to.code.length != 0)\n require(\n ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, data) ==\n ERC721TokenReceiver.onERC721Received.selector,\n\"UNSAFE_RECIPIENT\"\n );\n }\n}\n\n/// @notice A generic interface for a contract which properly accepts ERC721 tokens.\n/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC721.sol)\nabstract contract ERC721TokenReceiver {\n function onERC721Received(\n address,\n address,\n uint256,\n bytes calldata\n ) external virtual returns (bytes4) {\n return ERC721TokenReceiver.onERC721Received.selector;\n }\n}\n"
},
"contracts/universal/Transactor.sol":{
"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport { Owned } from \"@rari-capital/solmate/src/auth/Owned.sol\";\n\n/**\n * @title Transactor\n * @notice Transactor is a minimal contract that can send transactions.\n */\ncontract Transactor is Owned {\n /**\n * @param _owner Initial contract owner.\n */\n constructor(address _owner) Owned(_owner) {}\n\n /**\n * Sends a CALL to a target address.\n *\n * @param _target Address to call.\n * @param _data Data to send with the call.\n * @param _value ETH value to send with the call.\n *\n * @return Boolean success value.\n * @return Bytes data returned by the call.\n */\n function CALL(\n address _target,\n bytes memory _data,\n uint256 _value\n ) external payable onlyOwner returns (bool, bytes memory) {\n return _target.call{ value: _value }(_data);\n }\n\n /**\n * Sends a DELEGATECALL to a target address.\n *\n * @param _target Address to call.\n * @param _data Data to send with the call.\n *\n * @return Boolean success value.\n * @return Bytes data returned by the call.\n */\n function DELEGATECALL(address _target, bytes memory _data)\n external\n payable\n onlyOwner\n returns (bool, bytes memory)\n {\n // slither-disable-next-line controlled-delegatecall\n return _target.delegatecall(_data);\n }\n}\n"
"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.16;\n\nimport { AssetReceiver } from \"../AssetReceiver.sol\";\nimport { IDripCheck } from \"./IDripCheck.sol\";\n\n/**\n * @title Drippie\n * @notice Drippie is a system for managing automated contract interactions. A specific interaction\n * is called a \"drip\" and can be executed according to some condition (called a dripcheck)\n * and an execution interval. Drips cannot be executed faster than the execution interval.\n * Drips can trigger arbitrary contract calls where the calling contract is this contract\n * address. Drips can also send ETH value, which makes them ideal for keeping addresses\n * sufficiently funded with ETH. Drippie is designed to be connected with smart contract\n * automation services so that drips can be executed automatically. However, Drippie is\n * specifically designed to be separated from these services so that trust assumptions are\n * better compartmentalized.\n */\ncontract Drippie is AssetReceiver {\n /**\n * @notice Enum representing different status options for a given drip.\n *\n * @custom:value NONE Drip does not exist.\n * @custom:value PAUSED Drip is paused and cannot be executed until reactivated.\n * @custom:value ACTIVE Drip is active and can be executed.\n * @custom:value ARCHIVED Drip is archived and can no longer be executed or reactivated.\n */\n enum DripStatus {\n NONE,\n PAUSED,\n ACTIVE,\n ARCHIVED\n }\n\n /**\n * @notice Represents a drip action.\n */\n struct DripAction {\n address payable target;\n bytes data;\n uint256 value;\n }\n\n /**\n * @notice Represents the configuration for a given drip.\n */\n struct DripConfig {\n bool reentrant;\n uint256 interval;\n IDripCheck dripcheck;\n bytes checkparams;\n DripAction[] actions;\n }\n\n /**\n * @notice Represents the state of an active drip.\n */\n struct DripState {\n DripStatus status;\n DripConfig config;\n uint256 last;\n uint256 count;\n }\n\n /**\n * @notice Emitted when a new drip is created.\n *\n * @param nameref Indexed name parameter (hashed).\n * @param name Unindexed name parameter (unhashed).\n * @param config Config for the created drip.\n */\n event DripCreated(\n // Emit name twice because indexed version is hashed.\n string indexed nameref,\n string name,\n DripConfig config\n );\n\n /**\n * @notice Emitted when a drip status is updated.\n *\n * @param nameref Indexed name parameter (hashed).\n * @param name Unindexed name parameter (unhashed).\n * @param status New drip status.\n */\n event DripStatusUpdated(\n // Emit name twice because indexed version is hashed.\n string indexed nameref,\n string name,\n DripStatus status\n );\n\n /**\n * @notice Emitted when a drip is executed.\n *\n * @param nameref Indexed name parameter (hashed).\n * @param name Unindexed name parameter (unhashed).\n * @param executor Address that executed the drip.\n * @param timestamp Time when the drip was executed.\n */\n event DripExecuted(\n // Emit name twice because indexed version is hashed.\n string indexed nameref,\n string name,\n address executor,\n uint256 timestamp\n );\n\n /**\n * @notice Maps from drip names to drip states.\n */\n mapping(string => DripState) public drips;\n\n /**\n * @param _owner Initial contract owner.\n */\n constructor(address _owner) AssetReceiver(_owner) {}\n\n /**\n * @notice Creates a new drip with the given name and configuration. Once created, drips cannot\n * be modified in any way (this is a security measure). If you want to update a drip,\n * simply pause (and potentially archive) the existing drip and create a new one.\n *\n * @param _name Name of the drip.\n * @param _config Configuration for the drip.\n */\n function create(string calldata _name, DripConfig calldata _config) external onlyOwner {\n // Make sure this drip doesn't already exist. We *must* guarantee that no other function\n // will ever set the status of a drip back to NONE after it's been created. This is why\n // archival is a separate status.\n require(\n drips[_name].status == DripStatus.NONE,\n\"Drippie: drip with that name already exists\"\n );\n\n // Validate the drip interval, only allowing an interval of zero if the drip has explicitly\n // been marked as reentrant. Prevents client-side bugs making a drip infinitely executable\n // within the same block (of course, restricted by gas limits).\n if (_config.reentrant) {\n require(\n _config.interval == 0,\n\"Drippie: if allowing reentrant drip, must set interval to zero\"\n );\n } else {\n require(\n _config.interval > 0,\n\"Drippie: interval must be greater than zero if drip is not reentrant\"\n );\n }\n\n // We initialize this way because Solidity won't let us copy arrays into storage yet.\n DripState storage state = drips[_name];\n state.status = DripStatus.PAUSED;\n state.config.reentrant = _config.reentrant;\n state.config.interval = _config.interval;\n state.config.dripcheck = _config.dripcheck;\n state.config.checkparams = _config.checkparams;\n\n // Solidity doesn't let us copy arrays into storage, so we push each array one by one.\n for (uint256 i = 0; i < _config.actions.length; i++) {\n state.config.actions.push(_config.actions[i]);\n }\n\n // Tell the world!\n emit DripCreated(_name, _name, _config);\n }\n\n /**\n * @notice Sets the status for a given drip. The behavior of this function depends on the\n * status that the user is trying to set. A drip can always move between ACTIVE and\n * PAUSED, but it can never move back to NONE and once ARCHIVED, it can never move back\n * to ACTIVE or PAUSED.\n *\n * @param _name Name of the drip to update.\n * @param _status New drip status.\n */\n function status(string calldata _name, DripStatus _status) external onlyOwner {\n // Make sure we can never set drip status back to NONE. A simple security measure to\n // prevent accidental overwrites if this code is ever updated down the line.\n require(\n _status != DripStatus.NONE,\n\"Drippie: drip status can never be set back to NONE after creation\"\n );\n\n // Load the drip status once to avoid unnecessary SLOADs.\n DripStatus curr = drips[_name].status;\n\n // Make sure the drip in question actually exists. Not strictly necessary but there doesn't\n // seem to be any clear reason why you would want to do this, and it may save some gas in\n // the case of a front-end bug.\n require(\n curr != DripStatus.NONE,\n\"Drippie: drip with that name does not exist and cannot be updated\"\n );\n\n // Once a drip has been archived, it cannot be un-archived. This is, after all, the entire\n // point of archiving a drip.\n require(\n curr != DripStatus.ARCHIVED,\n\"Drippie: drip with that name has been archived and cannot be updated\"\n );\n\n // Although not strictly necessary, we make sure that the status here is actually changing.\n // This may save the client some gas if there's a front-end bug and the user accidentally\n // tries to \"change\" the status to the same value as before.\n require(\n curr != _status,\n\"Drippie: cannot set drip status to the same status as its current status\"\n );\n\n // If the user is trying to archive this drip, make sure the drip has been paused. We do\n // not allow users to archive active drips so that the effects of this action are more\n // abundantly clear.\n if (_status == DripStatus.ARCHIVED) {\n require(\n curr == DripStatus.PAUSED,\n\"Drippie: drip must first be paused before being archived\"\n );\n }\n\n // If we made it here then we can safely update the status.\n drips[_name].status = _status;\n emit DripStatusUpdated(_name, _name, _status);\n }\n\n /**\n * @notice Checks if a given drip is executable.\n *\n * @param _name Drip to check.\n *\n * @return True if the drip is executable, reverts otherwise.\n */\n function executable(string calldata _name) public view returns (bool) {\n DripState storage state = drips[_name];\n\n // Only allow active drips to be executed, an obvious security measure.\n require(\n state.status == DripStatus.ACTIVE,\n\"Drippie: selected drip does not exist or is not currently active\"\n );\n\n // Don't drip if the drip interval has not yet elapsed since the last time we dripped. This\n // is a safety measure that prevents a malicious recipient from, e.g., spending all of\n // their funds and repeatedly requesting new drips. Limits the potential impact of a\n // compromised recipient to just a single drip interval, after which the drip can be paused\n // by the owner address.\n require(\n state.last + state.config.interval <= block.timestamp,\n\"Drippie: drip interval has not elapsed since last drip\"\n );\n\n // Make sure we're allowed to execute this drip.\n require(\n state.config.dripcheck.check(state.config.checkparams),\n\"Drippie: dripcheck failed so drip is not yet ready to be triggered\"\n );\n\n // Alright, we're good to execute.\n return true;\n }\n\n /**\n * @notice Triggers a drip. This function is deliberately left as a public function because the\n * assumption being made here is that setting the drip to ACTIVE is an affirmative\n * signal that the drip should be executable according to the drip parameters, drip\n * check, and drip interval. Note that drip parameters are read entirely from the state\n * and are not supplied as user input, so there should not be any way for a\n * non-authorized user to influence the behavior of the drip. Note that the drip check\n * is executed only **once** at the beginning of the call to the drip function and will\n * not be executed again between the drip actions within this call.\n *\n * @param _name Name of the drip to trigger.\n */\n function drip(string calldata _name) external {\n DripState storage state = drips[_name];\n\n // Make sure the drip can be executed. Since executable reverts if the drip is not ready to\n // be executed, we don't need to do an assertion that the returned value is true.\n executable(_name);\n\n // Update the last execution time for this drip before the call. Note that it's entirely\n // possible for a drip to be executed multiple times per block or even multiple times\n // within the same transaction (via re-entrancy) if the drip interval is set to zero. Users\n // should set a drip interval of 1 if they'd like the drip to be executed only once per\n // block (since this will then prevent re-entrancy).\n state.last = block.timestamp;\n\n // Update the number of times this drip has been executed. Although this increases the cost\n // of using Drippie, it slightly simplifies the client-side by not having to worry about\n // counting drips via events. Useful for monitoring the rate of drip execution.\n state.count++;\n\n // Execute each action in the drip. We allow drips to have multiple actions because there\n // are scenarios in which a contract must do multiple things atomically. For example, the\n // contract may need to withdraw ETH from one account and then deposit that ETH into\n // another account within the same transaction.\n uint256 len = state.config.actions.length;\n for (uint256 i = 0; i < len; i++) {\n // Must be marked as \"storage\" because copying structs into memory is not yet supported\n // by Solidity. Won't significantly reduce gas costs but at least makes it easier to\n // read what the rest of this section is doing.\n DripAction storage action = state.config.actions[i];\n\n // Actually execute the action. We could use ExcessivelySafeCall here but not strictly\n // necessary (worst case, a drip gets bricked IFF the target is malicious, doubt this\n // will ever happen in practice). Could save a marginal amount of gas to ignore the\n // returndata.\n // slither-disable-next-line calls-loop\n (bool success, ) = action.target.call{ value: action.value }(action.data);\n\n // Generally should not happen, but could if there's a misconfiguration (e.g., passing\n // the wrong data to the target contract), the recipient is not payable, or\n // insufficient gas was supplied to this transaction. We revert so the drip can be\n // fixed and triggered again later. Means we cannot emit an event to alert of the\n // failure, but can reasonably be detected by off-chain services even without an event.\n // Note that this forces the drip executor to supply sufficient gas to the call\n // (assuming there is some sufficient gas limit that exists, otherwise the drip will\n // not execute).\n require(\n success,\n\"Drippie: drip was unsuccessful, please check your configuration for mistakes\"\n );\n }\n\n emit DripExecuted(_name, _name, msg.sender, block.timestamp);\n }\n}\n"
},
"contracts/universal/drippie/IDripCheck.sol":{
"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IDripCheck {\n // DripCheck contracts that want to take parameters as inputs MUST expose a struct called\n // Params and an event _EventForExposingParamsStructInABI(Params params). This makes it\n // possible to easily encode parameters on the client side. Solidity does not support generics\n // so it's not possible to do this with explicit typing.\n\n /**\n * @notice Checks whether a drip should be executable.\n *\n * @param _params Encoded parameters for the drip check.\n *\n * @return Whether the drip should be executed.\n */\n function check(bytes memory _params) external view returns (bool);\n}\n"
"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.16;\n\nimport { IDripCheck } from \"../IDripCheck.sol\";\n\n/**\n * @title CheckBalanceHigh\n * @notice DripCheck for checking if an account's balance is above a given threshold.\n */\ncontract CheckBalanceHigh is IDripCheck {\n struct Params {\n address target;\n uint256 threshold;\n }\n\n /**\n * @notice External event used to help client-side tooling encode parameters.\n *\n * @param params Parameters to encode.\n */\n event _EventToExposeStructInABI__Params(Params params);\n\n /**\n * @inheritdoc IDripCheck\n */\n function check(bytes memory _params) external view returns (bool) {\n Params memory params = abi.decode(_params, (Params));\n\n // Check target balance is above threshold.\n return params.target.balance > params.threshold;\n }\n}\n"
},
"contracts/testing/helpers/TestERC721.sol":{
"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport { ERC721 } from \"@rari-capital/solmate/src/tokens/ERC721.sol\";\n\ncontract TestERC721 is ERC721 {\n constructor() ERC721(\"TEST\", \"TST\") {}\n\n function mint(address to, uint256 tokenId) public {\n _mint(to, tokenId);\n }\n\n function tokenURI(uint256) public pure virtual override returns (string memory) {}\n}\n"