-
Notifications
You must be signed in to change notification settings - Fork 7
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Contracts separation p.1 #103
base: develop
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,179 @@ | ||
//SPDX-License-Identifier: Unlicense | ||
pragma solidity >=0.7.6; | ||
import "hardhat/console.sol"; | ||
|
||
contract ServiceRegistry { | ||
mapping(bytes32 => uint256) public lastExecuted; | ||
mapping(address => bool) private trustedAddresses; | ||
mapping(bytes32 => address) private namedService; | ||
address public owner; | ||
|
||
uint256 public requiredDelay = 0; //big enaugh that any power of miner over timestamp does not matter | ||
|
||
modifier validateInput(uint256 len) { | ||
require(msg.data.length == len, "illegal-padding"); | ||
_; | ||
} | ||
|
||
modifier delayedExecution() { | ||
bytes32 operationHash = keccak256(msg.data); | ||
uint256 reqDelay = requiredDelay; | ||
|
||
if (lastExecuted[operationHash] == 0 && reqDelay > 0) { | ||
//not called before, scheduled for execution | ||
// solhint-disable-next-line not-rely-on-time | ||
lastExecuted[operationHash] = block.timestamp; | ||
emit ChangeScheduled( | ||
msg.data, | ||
operationHash, | ||
// solhint-disable-next-line not-rely-on-time | ||
block.timestamp + reqDelay | ||
); | ||
} else { | ||
require( | ||
// solhint-disable-next-line not-rely-on-time | ||
block.timestamp - reqDelay > lastExecuted[operationHash], | ||
"delay-to-small" | ||
); | ||
// solhint-disable-next-line not-rely-on-time | ||
emit ChangeApplied(msg.data, block.timestamp); | ||
_; | ||
lastExecuted[operationHash] = 0; | ||
} | ||
} | ||
|
||
modifier onlyOwner() { | ||
require(msg.sender == owner, "only-owner"); | ||
_; | ||
} | ||
|
||
constructor(uint256 initialDelay) { | ||
require(initialDelay < type(uint256).max,"risk-of-overflow"); | ||
requiredDelay = initialDelay; | ||
owner = msg.sender; | ||
} | ||
|
||
function transferOwnership(address newOwner) | ||
public | ||
onlyOwner | ||
validateInput(36) | ||
delayedExecution | ||
{ | ||
owner = newOwner; | ||
} | ||
|
||
function changeRequiredDelay(uint256 newDelay) | ||
public | ||
onlyOwner | ||
validateInput(36) | ||
delayedExecution | ||
{ | ||
requiredDelay = newDelay; | ||
} | ||
|
||
function addTrustedAddress(address trustedAddress) | ||
public | ||
onlyOwner | ||
validateInput(36) | ||
delayedExecution | ||
{ | ||
trustedAddresses[trustedAddress] = true; | ||
} | ||
|
||
function removeTrustedAddress(address trustedAddress) | ||
public | ||
onlyOwner | ||
validateInput(36) | ||
{ | ||
trustedAddresses[trustedAddress] = false; | ||
} | ||
|
||
function isTrusted(address testedAddress) public view returns (bool) { | ||
return trustedAddresses[testedAddress]; | ||
} | ||
|
||
function getServiceNameHash(string memory name) | ||
public | ||
pure | ||
returns (bytes32) | ||
{ | ||
return keccak256(abi.encodePacked(name)); | ||
} | ||
|
||
function addNamedService(bytes32 serviceNameHash, address serviceAddress) | ||
public | ||
onlyOwner | ||
validateInput(68) | ||
delayedExecution | ||
{ | ||
require( | ||
namedService[serviceNameHash] == address(0), | ||
"service-override" | ||
); | ||
namedService[serviceNameHash] = serviceAddress; | ||
} | ||
|
||
function updateNamedService(bytes32 serviceNameHash, address serviceAddress) | ||
public | ||
onlyOwner | ||
validateInput(68) | ||
delayedExecution | ||
{ | ||
require( | ||
namedService[serviceNameHash] != address(0), | ||
"service-does-not-exist" | ||
); | ||
namedService[serviceNameHash] = serviceAddress; | ||
} | ||
|
||
function removeNamedService(bytes32 serviceNameHash) | ||
public | ||
onlyOwner | ||
validateInput(36) | ||
{ | ||
require( | ||
namedService[serviceNameHash] != address(0), | ||
"service-does-not-exist" | ||
); | ||
namedService[serviceNameHash] = address(0); | ||
emit RemoveApplied(serviceNameHash); | ||
} | ||
|
||
function getRegisteredService(string memory serviceName) | ||
public | ||
view | ||
returns (address) | ||
{ | ||
address retVal = getServiceAddress( | ||
keccak256(abi.encodePacked(serviceName)) | ||
); | ||
return retVal; | ||
} | ||
|
||
function getServiceAddress(bytes32 serviceNameHash) | ||
public | ||
view | ||
returns (address) | ||
{ | ||
return namedService[serviceNameHash]; | ||
} | ||
|
||
function clearScheduledExecution(bytes32 scheduledExecution) | ||
public | ||
onlyOwner | ||
validateInput(36) | ||
{ | ||
require(lastExecuted[scheduledExecution] > 0, "execution-not-sheduled"); | ||
lastExecuted[scheduledExecution] = 0; | ||
emit ChangeCancelled(scheduledExecution); | ||
} | ||
|
||
event ChangeScheduled( | ||
bytes data, | ||
bytes32 dataHash, | ||
uint256 firstPossibleExecutionTime | ||
); | ||
event ChangeCancelled(bytes32 data); | ||
event ChangeApplied(bytes data, uint256 firstPossibleExecutionTime); | ||
event RemoveApplied(bytes32 nameHash); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
// SPDX-License-Identifier: AGPL-3.0-or-later | ||
pragma solidity >=0.7.6; | ||
pragma abicoder v2; | ||
|
||
abstract contract ActionBase { | ||
enum ActionType { | ||
FLASHLOAN, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see what You tried to do with this,
but maybe there is really only one, and fact that operaation makes a callback is its internal detail as long as valle address is passed as parameter |
||
DEFAULT | ||
} | ||
|
||
function executeAction(bytes[] memory _callData) public payable virtual returns (bytes32); | ||
|
||
function actionType() public pure virtual returns (uint8); | ||
} |
Original file line number | Diff line number | Diff line change | ||
---|---|---|---|---|
@@ -0,0 +1,58 @@ | ||||
// SPDX-License-Identifier: AGPL-3.0-or-later | ||||
pragma solidity >=0.7.6; | ||||
pragma abicoder v2; | ||||
|
||||
import "../interfaces/mcd/IJoin.sol"; | ||||
import "../interfaces/mcd/IManager.sol"; | ||||
import "./ActionBase.sol"; | ||||
import "hardhat/console.sol"; | ||||
import "../ServiceRegistry.sol"; | ||||
|
||||
struct Operation { | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. in 0.8+ solc and in 0.7 with abie encoder v2 pragma constructs like:
are possible |
||||
string name; | ||||
bytes[][] callData; | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why it is multidimentional? I'm quite sure we can get away with one dimention and then do abi.decode
generally it is reasonable to assume that action knows format of its parameters, so You pass bytes to action and it decodes it in any way it wants |
||||
bytes32[] actionIds; | ||||
address serviceRegistryAddr; | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. serviceRegistryAddress can be immutable instead I guess it will be allways the same |
||||
} | ||||
|
||||
contract OperationRunner { | ||||
function executeOperation(Operation memory operation) public payable { | ||||
_executeActions(operation); | ||||
} | ||||
|
||||
function _executeActions(Operation memory operation) internal { | ||||
// address firstActionAddr = ServiceRegistry(operation.serviceRegistryAddr) | ||||
// .getServiceAddress(operation.actionIds[0]); | ||||
|
||||
bytes32[] memory returnValues = new bytes32[](operation.actionIds.length); | ||||
|
||||
// if (isFlashLoanAction(firstActionAddr)) { | ||||
// executeFlashloan(operation, firstActionAddr, returnValues); | ||||
// } else { | ||||
for (uint256 i = 0; i < operation.actionIds.length; ++i) { | ||||
returnValues[i] = _executeAction(operation, i, returnValues); | ||||
// _executeAction(operation, i); | ||||
} | ||||
// } | ||||
} | ||||
|
||||
function _executeAction( | ||||
Operation memory operation, | ||||
uint256 _index, | ||||
bytes32[] memory returnValues | ||||
) internal returns (bytes32 response) { | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if we are about to support return values, i think it should be bytes not bytes32 |
||||
address actionAddress = ServiceRegistry(operation.serviceRegistryAddr).getServiceAddress( | ||||
operation.actionIds[_index] | ||||
); | ||||
|
||||
actionAddress.delegatecall( | ||||
abi.encodeWithSignature("executeAction(bytes[])", operation.callData[_index]) | ||||
); | ||||
|
||||
return ""; | ||||
} | ||||
|
||||
function isFlashLoanAction(address actionAddr) internal pure returns (bool) { | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is it used after all? |
||||
return ActionBase(actionAddr).actionType() == uint8(ActionBase.ActionType.FLASHLOAN); | ||||
} | ||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
// SPDX-License-Identifier: AGPL-3.0-or-later | ||
pragma solidity >=0.7.6; | ||
pragma abicoder v2; | ||
|
||
import "../interfaces/mcd/IJoin.sol"; | ||
import "../interfaces/mcd/IManager.sol"; | ||
import "./ActionBase.sol"; | ||
import "hardhat/console.sol"; | ||
|
||
contract Deposit is ActionBase { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this a right name ? looks like open empty vault to me |
||
function executeAction(bytes[] memory _callData) | ||
public | ||
payable | ||
override | ||
returns ( | ||
// ) public payable virtual override returns (bytes32) { | ||
bytes32 | ||
) | ||
{ | ||
(address joinAddr, address mcdManager) = parseInputs(_callData); | ||
// address joinAddr = 0x2F0b23f53734252Bda2277357e97e1517d6B042A; | ||
// address mcdManager = 0x5ef30b9986345249bc32d8928B7ee64DE9435E39; | ||
|
||
// // joinAddr = _parseParamAddr(joinAddr, _paramMapping[0], _subData, _returnValues); | ||
|
||
console.log("address this", address(this)); | ||
|
||
uint256 newVaultId = _mcdOpen(joinAddr, mcdManager); | ||
|
||
return bytes32(newVaultId); | ||
} | ||
|
||
function actionType() public pure override returns (uint8) { | ||
return uint8(ActionType.DEFAULT); | ||
} | ||
|
||
function _mcdOpen(address _joinAddr, address _mcdManager) internal returns (uint256 vaultId) { | ||
bytes32 ilk = IJoin(_joinAddr).ilk(); | ||
vaultId = IManager(_mcdManager).open(ilk, address(this)); | ||
} | ||
|
||
function parseInputs(bytes[] memory _callData) | ||
internal | ||
pure | ||
returns (address joinAddr, address mcdManager) | ||
{ | ||
joinAddr = abi.decode(_callData[0], (address)); | ||
mcdManager = abi.decode(_callData[1], (address)); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
// SPDX-License-Identifier: AGPL-3.0-or-later | ||
pragma solidity >=0.7.6; | ||
pragma abicoder v2; | ||
|
||
import "../interfaces/mcd/IJoin.sol"; | ||
import "../interfaces/mcd/IManager.sol"; | ||
import "./ActionBase.sol"; | ||
import "hardhat/console.sol"; | ||
|
||
contract Flashloan is ActionBase { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess this is just placeholder, flashloan can be (I think ) just and Operation like any other, it takes some some parameters that describes flash loan and then another operation which it executes on a runner. |
||
function actionType() public pure override returns (uint8) { | ||
return uint8(ActionType.FLASHLOAN); | ||
} | ||
|
||
function executeAction(bytes[] memory _callData) | ||
public | ||
payable | ||
override | ||
returns ( | ||
// ) public payable virtual override returns (bytes32) { | ||
bytes32 | ||
) | ||
{ | ||
return ""; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
// SPDX-License-Identifier: AGPL-3.0-or-later | ||
pragma solidity >=0.7.6; | ||
pragma abicoder v2; | ||
|
||
import "../interfaces/mcd/IJoin.sol"; | ||
import "../interfaces/mcd/IManager.sol"; | ||
import "./ActionBase.sol"; | ||
import "hardhat/console.sol"; | ||
|
||
contract OpenVault is ActionBase { | ||
function executeAction(bytes[] memory _callData) public payable override returns (bytes32) { | ||
// (address joinAddr, address mcdManager) = parseInputs(_callData); | ||
|
||
address joinAddr = 0x2F0b23f53734252Bda2277357e97e1517d6B042A; | ||
address mcdManager = 0x5ef30b9986345249bc32d8928B7ee64DE9435E39; | ||
|
||
uint256 newVaultId = _mcdOpen(joinAddr, mcdManager); | ||
|
||
return bytes32(newVaultId); | ||
} | ||
|
||
function actionType() public pure override returns (uint8) { | ||
return uint8(ActionType.DEFAULT); | ||
} | ||
|
||
function _mcdOpen(address _joinAddr, address _mcdManager) internal returns (uint256 vaultId) { | ||
bytes32 ilk = IJoin(_joinAddr).ilk(); | ||
vaultId = IManager(_mcdManager).open(ilk, address(this)); | ||
} | ||
|
||
function parseInputs(bytes[] memory _callData) | ||
internal | ||
pure | ||
returns (address joinAddr, address mcdManager) | ||
{ | ||
joinAddr = abi.decode(_callData[0], (address)); | ||
mcdManager = abi.decode(_callData[1], (address)); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
try defaulting to
external
visibility where possible. see https://ethereum.stackexchange.com/questions/19380/external-vs-public-best-practicesThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
there is a pending PR on automation repo to change all
ServiceRegistry
visibility as well