-
Notifications
You must be signed in to change notification settings - Fork 5
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
feat: OwnerProxy #116
feat: OwnerProxy #116
Conversation
test/unit/OwnerProxy.unit.ts
Outdated
).to.be.revertedWith("NF: ZERO_FEES"); | ||
}); | ||
|
||
it("Cant update fees if wrong exit fees is zero", async () => { |
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.
it("Cant update fees if wrong exit fees is zero", async () => { | |
it("Cant update fees if exit fees are zero", async () => { |
test/unit/OwnerProxy.unit.ts
Outdated
}); | ||
|
||
describe("Update fees", () => { | ||
it("Cant update fees if wrong entry fees is zero", async () => { |
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.
it("Cant update fees if wrong entry fees is zero", async () => { | |
it("Cant update fees if entry fees are zero", async () => { |
}); | ||
}); | ||
|
||
describe("Add operator", () => { |
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.
Add a test that should revert if the calldata NestedFactory address is zero
IOperatorResolver.Operator memory operator, | ||
bytes32 name | ||
) external { | ||
IOperatorResolver resolver = IOperatorResolver(MixinOperatorResolver(nestedFactory).resolver()); |
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.
In case the address of the NestedFactory is not correct, wouldn't it be cheaper in gas to make a require to check it first?
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.
Not really, but we must check the operator and factory address before
contracts/governance/OwnerProxy.sol
Outdated
contract OwnerProxy is Ownable { | ||
/// @notice Execute atomic actions. Only the owner can call this function (e.g. the timelock) | ||
/// @param _target Address of the "script" to perform a delegatecall | ||
/// @param _data The bytes calldata (bytes4 selector must be included) |
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.
(bytes4 selector must be included)
should be a /// @dev
imo
import "../../interfaces/IOperatorResolver.sol"; | ||
import "../../abstracts/MixinOperatorResolver.sol"; | ||
|
||
contract AddOperator { |
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.
Could be great to also have the RemoveOperator ready to prevent making mistakes if we ever need to do it urgently.
test/unit/OwnerProxy.unit.ts
Outdated
.execute(scriptAddOperator.address, scriptAddOperatorCalldata); | ||
}); | ||
|
||
it("Cant remove operator if nested factory address is zero", async () => { |
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.
it("Cant remove operator if nested factory address is zero", async () => { | |
it("Cannot remove operator if nested factory address is zero", async () => { |
or
it("Cant remove operator if nested factory address is zero", async () => { | |
it("Can't remove operator if nested factory address is zero", async () => { |
IOperatorResolver resolver = IOperatorResolver(MixinOperatorResolver(nestedFactory).resolver()); | ||
|
||
// Init arrays | ||
uint256 operatorLength = operators.length; |
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.
uint256 operatorLength = operators.length; | |
uint256 operatorLength = operators.length; | |
require(operatorLength != 0, "DAO-SCRIPT: INVALID_OPERATOR_LEN"); |
operators[i].selector | ||
); | ||
names[i] = operators[i].name; | ||
operatorsToImport[i] = operator; |
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.
Maybe store the new IOperatorResolver.Operator directly in the operatorsToImport array instead of storing it in a memory cached variable and then store it in the array, can save some gas.
operatorsToImport[i] = operator; | |
operatorsToImport[i] = IOperatorResolver.Operator( | |
deployedAddress, | |
operators[i].selector | |
);; |
INestedFactory(nestedFactory).addOperator(operators[i].name); | ||
} | ||
|
||
require(MixinOperatorResolver(nestedFactory).isResolverCached(), "DAO-SCRIPT: UNRESOLVED_CACHE"); |
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.
I am not very familiar with the MixinOperatorResolver, I wonder if this line is absolutely mandatory?
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.
Not mandatory, but good to have
bytes memory bytecode, | ||
tupleOperator[] memory operators | ||
) external { | ||
require(nestedFactory != address(0), "DAO-SCRIPT: INVALID_FACTORY_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.
Change code errors to sting < 32 bytes. I know it's not important here, but it will be raised in audits later.
bytes memory bytecode, | ||
tupleOperator[] memory operators |
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.
It's a bit confusing:
- method name is singular
- one bytecode as param
but:
operators
param as an array - plural
Consider renaming operators
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.
In the OperatorResolver
, an Operator is an address
(implementation) and a bytes4
(selector), linked to a bytes32
(name).
The bytecode
param will be deployed and as a result, the implementation (address).
So, the operators
is the array with name
and selector
, it is really an "operator" list to add.
I don't know how we could rename operators
. Any idea ?
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.
I would remove tuple from the tupleOperator
name, it is explicit by looking at the type, no ?
Maybe name the struct operatorFunction
and the array operatorFunctions
?
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.
I understand how it works, then the method name is inaccurate, and should be plural.
Either the operator is the contract, or it's the address + selector + bytes. It just needs to be consistent across the protocol
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.
@adrien-supizet I misunderstood, I'm updating to DeployAddOperators
refactor: remove unused imports
refactor: merge operator scripts
operatorsToImport[0] = IOperatorResolver.Operator({ implementation: address(0), selector: bytes4(0) }); | ||
destinations[0] = MixinOperatorResolver(nestedFactory); | ||
|
||
IOperatorResolver(resolver).importOperators(names, operatorsToImport, destinations); |
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.
importOperators
It does not look like we're removing an operator here. Am I missing something?
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.
I'm missing something here, I'll take a look at NestedFactory's removeOperator method before finishing the review
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.
Ok, I think the OperatorResolver should have had a removeOperators method, but this works too.
In order to complete the ownership architecture, we need the
OwnerProxy
contract in charge of executing scripts for the Timelock (run transactions atomically).To show how the contract works, I added two scripts :
AddOperator.sol
, to add an operator (import, add and rebuild cache)UpdateFees
, to update the entry and exit fees.We can also use a single OwnerProxy, but it must use
upgradeToAndCall
to call the NestedFactory owner functions :