-
Notifications
You must be signed in to change notification settings - Fork 3
Budget modules base #75
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
Merged
Merged
Changes from all commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
e9eee53
Budget: recurrent payment module spike
izqui 97d75a4
Delete dead code
izqui f3b9487
Split BudgetModule
izqui 0406f40
Optimize
izqui 65cf20c
Budget: disallow changing amount of top-level allowance to 0 and expl…
izqui 0116bd1
RecurringPayments: disallow creating payments with 0 amount
izqui 4e4261f
RecurringPayments: events and errors
izqui cd05c50
BudgetModule: initialize on constructor
izqui ca5d63a
UpgradeableModuleProxyFactory: registry (#80)
izqui d2dadbd
General: make event naming consistent (#82)
Evalir 8516175
Remove RecurringPayments from branch scope
izqui 55af06c
Remove Recurring Payments integration test
izqui 2226387
BudgetModule: unstructured storage
izqui File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,4 @@ | ||
| // SPDX-License-Identifier: UNLICENSED | ||
| pragma solidity ^0.8.16; | ||
|
|
||
| import {SafeAware} from "./SafeAware.sol"; | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| // SPDX-License-Identifier: UNLICENSED | ||
| pragma solidity 0.8.16; | ||
|
|
||
| import {Budget} from "../Budget.sol"; | ||
| import {FirmBase, IAvatar} from "../../bases/FirmBase.sol"; | ||
|
|
||
| address constant IMPL_INIT_ADDRESS = address(1); | ||
|
|
||
| abstract contract BudgetModule is FirmBase { | ||
| // BUDGET_SLOT = keccak256("firm.budgetmodule.budget") - 1 | ||
| bytes32 internal constant BUDGET_SLOT = 0xc7637e5414363c2355f9e835e00d15501df0666fb3c6c5fe259b9a40aeedbc49; | ||
|
|
||
| constructor() { | ||
| // Initialize with impossible values in constructor so impl base cannot be used | ||
| initialize(Budget(IMPL_INIT_ADDRESS), IMPL_INIT_ADDRESS); | ||
| } | ||
|
|
||
| function initialize(Budget budget_, address trustedForwarder_) public { | ||
| IAvatar safe = address(budget_) != IMPL_INIT_ADDRESS ? budget_.safe() : IAvatar(IMPL_INIT_ADDRESS); | ||
| __init_firmBase(safe, trustedForwarder_); | ||
| assembly { | ||
| sstore(BUDGET_SLOT, budget_) | ||
| } | ||
| } | ||
|
|
||
| function budget() public view returns (Budget _budget) { | ||
| assembly { | ||
| _budget := sload(BUDGET_SLOT) | ||
| } | ||
| } | ||
|
|
||
| error UnauthorizedNotAllowanceAdmin(uint256 allowanceId, address actor); | ||
|
|
||
| modifier onlyAllowanceAdmin(uint256 allowanceId) { | ||
| address actor = _msgSender(); | ||
| if (!budget().isAdminOnAllowance(allowanceId, actor)) { | ||
| revert UnauthorizedNotAllowanceAdmin(allowanceId, actor); | ||
| } | ||
|
|
||
| _; | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,43 +1,84 @@ | ||
| // SPDX-License-Identifier: UNLICENSED | ||
| pragma solidity 0.8.16; | ||
|
|
||
| import {EIP1967Upgradeable} from "../bases/EIP1967Upgradeable.sol"; | ||
| import {Ownable} from "openzeppelin/access/Ownable.sol"; | ||
| import {IModuleMetadata} from "../bases/IModuleMetadata.sol"; | ||
|
|
||
| contract UpgradeableModuleProxyFactory { | ||
| error TakenAddress(address proxy); | ||
| uint256 constant LATEST_VERSION = type(uint256).max; | ||
|
|
||
| contract UpgradeableModuleProxyFactory is Ownable { | ||
| error ProxyAlreadyDeployedForNonce(); | ||
| error FailedInitialization(); | ||
| error ModuleVersionAlreadyRegistered(); | ||
| error UnexistentModuleVersion(); | ||
|
|
||
| event ModuleProxyCreation(address indexed proxy, EIP1967Upgradeable indexed implementation); | ||
| event ModuleRegistered(IModuleMetadata indexed implementation, string moduleId, uint256 version); | ||
| event ModuleProxyCreated(address indexed proxy, IModuleMetadata indexed implementation); | ||
|
|
||
| function createUpgradeableProxy(EIP1967Upgradeable implementation, bytes32 salt) internal returns (address addr) { | ||
| // if (address(target) == address(0)) revert ZeroAddress(target); | ||
| // Removed as this is a responsibility of the caller and we shouldn't pay for the check on each proxy creation | ||
| bytes memory initcode = abi.encodePacked( | ||
| hex"73", | ||
| implementation, | ||
| hex"7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc55603b8060403d393df3363d3d3760393d3d3d3d3d363d7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc545af4913d913e3d9257fd5bf3" | ||
| ); | ||
| mapping(string => mapping(uint256 => IModuleMetadata)) internal modules; | ||
| mapping(string => uint256) public latestModuleVersion; | ||
|
|
||
| assembly { | ||
| addr := create2(0, add(initcode, 0x20), mload(initcode), salt) | ||
| function register(IModuleMetadata implementation) external onlyOwner { | ||
| string memory moduleId = implementation.moduleId(); | ||
| uint256 version = implementation.moduleVersion(); | ||
|
|
||
| if (address(modules[moduleId][version]) != address(0)) { | ||
| revert ModuleVersionAlreadyRegistered(); | ||
| } | ||
|
|
||
| if (addr == address(0)) { | ||
| revert TakenAddress(addr); | ||
| modules[moduleId][version] = implementation; | ||
|
|
||
| if (version > latestModuleVersion[moduleId]) { | ||
| latestModuleVersion[moduleId] = version; | ||
| } | ||
|
|
||
| emit ModuleRegistered(implementation, moduleId, version); | ||
| } | ||
|
|
||
| function getImplementation(string memory moduleId, uint256 version) public view returns (IModuleMetadata implementation) { | ||
| if (version == LATEST_VERSION) { | ||
| version = latestModuleVersion[moduleId]; | ||
| } | ||
| implementation = modules[moduleId][version]; | ||
| if (address(implementation) == address(0)) { | ||
| revert UnexistentModuleVersion(); | ||
| } | ||
| } | ||
|
|
||
| function deployUpgradeableModule(EIP1967Upgradeable implementation, bytes memory initializer, uint256 salt) | ||
| function deployUpgradeableModule(string memory moduleId, uint256 version, bytes memory initializer, uint256 salt) | ||
| public | ||
| returns (address proxy) | ||
| { | ||
| proxy = createUpgradeableProxy(implementation, keccak256(abi.encodePacked(keccak256(initializer), salt))); | ||
| return deployUpgradeableModule(getImplementation(moduleId, version), initializer, salt); | ||
| } | ||
|
|
||
| function deployUpgradeableModule(IModuleMetadata implementation, bytes memory initializer, uint256 salt) | ||
| public | ||
| returns (address proxy) | ||
| { | ||
| proxy = createProxy(implementation, keccak256(abi.encodePacked(keccak256(initializer), salt))); | ||
|
|
||
| (bool success,) = proxy.call(initializer); | ||
| if (!success) { | ||
| revert FailedInitialization(); | ||
| } | ||
| } | ||
|
|
||
| function createProxy(IModuleMetadata implementation, bytes32 salt) internal returns (address proxy) { | ||
| bytes memory initcode = abi.encodePacked( | ||
| hex"73", | ||
| implementation, | ||
| hex"7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc55603b8060403d393df3363d3d3760393d3d3d3d3d363d7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc545af4913d913e3d9257fd5bf3" | ||
| ); | ||
|
|
||
| assembly { | ||
| proxy := create2(0, add(initcode, 0x20), mload(initcode), salt) | ||
| } | ||
|
|
||
| if (proxy == address(0)) { | ||
| revert ProxyAlreadyDeployedForNonce(); | ||
| } | ||
|
|
||
| emit ModuleProxyCreation(proxy, implementation); | ||
| emit ModuleProxyCreated(proxy, implementation); | ||
| } | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.