Conversation
added update spending limit functionality
fix bugs about fixed size memory struct
mehrdadmms
left a comment
There was a problem hiding this comment.
Please check the suggestions and then test. I didn't test the code just added the suggestions.
| /// @notice Initializes the contract. | ||
| /// @param _dao The associated DAO. | ||
| /// @param _hatId The id of the hat. | ||
| function initialize(IDAO _dao, uint256 _hatId, uint256 _spendingLimitETH) external initializer { | ||
| function initialize(IDAO _dao, uint256 _hatId,address[] memory _token ,uint256[] memory _spendingLimit) external initializer { | ||
| __PluginCloneable_init(_dao); | ||
| hatId = _hatId; | ||
| // TODO get this from environment per network (this is goerli) | ||
| hatsProtocolInstance = IHats(0x3bc1A0Ad72417f2d411118085256fC53CBdDd137); | ||
| spendingLimitETH = _spendingLimitETH; | ||
| require(_token.length==_spendingLimit.length,"Length of token address and spendingLimit array is not equal"); | ||
| // spendingLimit = _spendingLimit; | ||
| for (uint j=0; j < _token.length; j+=1) { | ||
| spendingLimit[_token[j]]=_spendingLimit[j]; | ||
| } | ||
| availableTokens=_token; | ||
| } |
There was a problem hiding this comment.
Is it possible to use an array of objects instead of 2 arrays?
| /// @notice Initializes the contract. | |
| /// @param _dao The associated DAO. | |
| /// @param _hatId The id of the hat. | |
| function initialize(IDAO _dao, uint256 _hatId, uint256 _spendingLimitETH) external initializer { | |
| function initialize(IDAO _dao, uint256 _hatId,address[] memory _token ,uint256[] memory _spendingLimit) external initializer { | |
| __PluginCloneable_init(_dao); | |
| hatId = _hatId; | |
| // TODO get this from environment per network (this is goerli) | |
| hatsProtocolInstance = IHats(0x3bc1A0Ad72417f2d411118085256fC53CBdDd137); | |
| spendingLimitETH = _spendingLimitETH; | |
| require(_token.length==_spendingLimit.length,"Length of token address and spendingLimit array is not equal"); | |
| // spendingLimit = _spendingLimit; | |
| for (uint j=0; j < _token.length; j+=1) { | |
| spendingLimit[_token[j]]=_spendingLimit[j]; | |
| } | |
| availableTokens=_token; | |
| } | |
| struct Budget { | |
| address token; | |
| uint256 spendingLimit; | |
| } | |
| /// @notice Initializes the contract. | |
| /// @param _dao The associated DAO. | |
| /// @param _hatId The id of the hat. | |
| /// @param _budget An array of Budgest struct which is an object consisted of token address and spending amount. | |
| function initialize(IDAO _dao, uint256 _hatId, Budget[] calldata _budget) external initializer { | |
| __PluginCloneable_init(_dao); | |
| hatId = _hatId; | |
| // TODO get this from environment per network (this is goerli) | |
| hatsProtocolInstance = IHats(0x3bc1A0Ad72417f2d411118085256fC53CBdDd137); | |
| for (uint j=0; j < _budget.length; j+=1) { | |
| spendingLimit[_budget[j].token] = _budget[j].spendingLimit; | |
| availableTokens = _budget[j].token | |
| } | |
| } | |
There was a problem hiding this comment.
I did it manually
| /// @notice Executes actions in the associated DAO. | ||
| /// @param _actions The actions to be executed by the DAO. | ||
| /// @param _myActions The actions to be executed by the DAO. | ||
| function execute( | ||
| IDAO.Action[] calldata _actions | ||
| MyAction[] calldata _myActions | ||
| ) external { | ||
| require(hatsProtocolInstance.isWearerOfHat(msg.sender, hatId), "Sender is not wearer of the hat"); | ||
| hasRemainingBudget(_actions); | ||
| dao().execute({_callId: 0x0, _actions: _actions, _allowFailureMap: 0}); | ||
| IDAO.Action [] memory idaoAction = hasRemainingBudget(_myActions); | ||
| dao().execute({_callId: 0x0, _actions: idaoAction, _allowFailureMap: 0}); | ||
| } |
There was a problem hiding this comment.
| /// @notice Executes actions in the associated DAO. | |
| /// @param _actions The actions to be executed by the DAO. | |
| /// @param _myActions The actions to be executed by the DAO. | |
| function execute( | |
| IDAO.Action[] calldata _actions | |
| MyAction[] calldata _myActions | |
| ) external { | |
| require(hatsProtocolInstance.isWearerOfHat(msg.sender, hatId), "Sender is not wearer of the hat"); | |
| hasRemainingBudget(_actions); | |
| dao().execute({_callId: 0x0, _actions: _actions, _allowFailureMap: 0}); | |
| IDAO.Action [] memory idaoAction = hasRemainingBudget(_myActions); | |
| dao().execute({_callId: 0x0, _actions: idaoAction, _allowFailureMap: 0}); | |
| } | |
| /// @notice Executes actions in the associated DAO. | |
| /// @param _workingCapitalActions The actions to be executed by the DAO. | |
| function execute( | |
| WorkingCapitalAction[] calldata _workingCapitalActions | |
| ) external { | |
| require(hatsProtocolInstance.isWearerOfHat(msg.sender, hatId), "Sender is not wearer of the hat"); | |
| IDAO.Action [] memory iDAOAction = hasRemainingBudget(_workingCapitalActions); | |
| dao().execute({_callId: 0x0, _actions: iDAOAction, _allowFailureMap: 0}); | |
| } |
| /// @notice Checking that can user withdraw this amount | ||
| /// @param _actions actions that would be checked | ||
| function hasRemainingBudget(IDAO.Action[] calldata _actions) internal { | ||
| /// @return generatedDAOActions IDAO.Action generated for use in execute | ||
| function hasRemainingBudget(MyAction[] calldata _actions) internal returns(IDAO.Action[] memory generatedDAOActions){ | ||
| uint _currentMonth = BokkyPooBahsDateTimeLibrary.getMonth(block.timestamp); | ||
| uint _currentYear = BokkyPooBahsDateTimeLibrary.getYear(block.timestamp); | ||
| uint j=0; | ||
| for (; j < _actions.length; j+=1) { //for loop example | ||
| generatedDAOActions = new IDAO.Action[](_actions.length); | ||
| for (uint j=0; j < _actions.length; j+=1) { | ||
| address _to; | ||
| uint256 _value; | ||
| bytes memory _data; | ||
| address _token; | ||
| if(_actions[j].ERC20 == address(0)){ | ||
| _to=_actions[j].to; | ||
| _value=_actions[j].value; | ||
| _data= new bytes(0); | ||
| _token=address(0); | ||
| require(isTokenAvailable(address(0)),"It is not available token in this plugin"); | ||
|
|
||
| } | ||
| else{ | ||
| _to=_actions[j].ERC20; | ||
| _value=0; | ||
| bytes memory data = abi.encodeWithSignature("transfer(address,uint256)", _actions[j].to, _actions[j].value); | ||
| _data= data; | ||
| _token=_actions[j].ERC20; | ||
| require(isTokenAvailable(_actions[j].ERC20),"It is not available token in this plugin"); | ||
|
|
||
| } | ||
| // if we are on the month that we were | ||
| if(_currentMonth==currentMonth && _currentYear==currentYear){ | ||
| require( | ||
| remainingBudget>=_actions[j].value, | ||
| remainingBudget[_token]>=_actions[j].value, | ||
| string.concat("In ",Strings.toString(j)," action you want to spend more than your limit monthly") | ||
| ); | ||
| remainingBudget-=_actions[j].value; | ||
| remainingBudget[_token] -=_actions[j].value; | ||
| } | ||
| // if we are on another month | ||
| else{ | ||
| currentYear = _currentYear; | ||
| currentMonth = _currentMonth; | ||
| remainingBudget=spendingLimitETH; | ||
| for (uint j=0; j < availableTokens.length; j+=1) { | ||
| remainingBudget[availableTokens[j]]=spendingLimit[availableTokens[j]]; | ||
| } | ||
| require( | ||
| remainingBudget>=_actions[j].value, | ||
| remainingBudget[_token]>=_actions[j].value, | ||
| string.concat("In ",Strings.toString(j)," action you want to spend more than your limit monthly") | ||
| ); | ||
| remainingBudget-=_actions[j].value; | ||
| remainingBudget[_token]-=_actions[j].value; | ||
| } | ||
|
|
||
| generatedDAOActions[j]=IDAO.Action(_to, _value, _data); | ||
|
|
||
| } | ||
| } |
There was a problem hiding this comment.
| /// @notice Checking that can user withdraw this amount | |
| /// @param _actions actions that would be checked | |
| function hasRemainingBudget(IDAO.Action[] calldata _actions) internal { | |
| /// @return generatedDAOActions IDAO.Action generated for use in execute | |
| function hasRemainingBudget(MyAction[] calldata _actions) internal returns(IDAO.Action[] memory generatedDAOActions){ | |
| uint _currentMonth = BokkyPooBahsDateTimeLibrary.getMonth(block.timestamp); | |
| uint _currentYear = BokkyPooBahsDateTimeLibrary.getYear(block.timestamp); | |
| uint j=0; | |
| for (; j < _actions.length; j+=1) { //for loop example | |
| generatedDAOActions = new IDAO.Action[](_actions.length); | |
| for (uint j=0; j < _actions.length; j+=1) { | |
| address _to; | |
| uint256 _value; | |
| bytes memory _data; | |
| address _token; | |
| if(_actions[j].ERC20 == address(0)){ | |
| _to=_actions[j].to; | |
| _value=_actions[j].value; | |
| _data= new bytes(0); | |
| _token=address(0); | |
| require(isTokenAvailable(address(0)),"It is not available token in this plugin"); | |
| } | |
| else{ | |
| _to=_actions[j].ERC20; | |
| _value=0; | |
| bytes memory data = abi.encodeWithSignature("transfer(address,uint256)", _actions[j].to, _actions[j].value); | |
| _data= data; | |
| _token=_actions[j].ERC20; | |
| require(isTokenAvailable(_actions[j].ERC20),"It is not available token in this plugin"); | |
| } | |
| // if we are on the month that we were | |
| if(_currentMonth==currentMonth && _currentYear==currentYear){ | |
| require( | |
| remainingBudget>=_actions[j].value, | |
| remainingBudget[_token]>=_actions[j].value, | |
| string.concat("In ",Strings.toString(j)," action you want to spend more than your limit monthly") | |
| ); | |
| remainingBudget-=_actions[j].value; | |
| remainingBudget[_token] -=_actions[j].value; | |
| } | |
| // if we are on another month | |
| else{ | |
| currentYear = _currentYear; | |
| currentMonth = _currentMonth; | |
| remainingBudget=spendingLimitETH; | |
| for (uint j=0; j < availableTokens.length; j+=1) { | |
| remainingBudget[availableTokens[j]]=spendingLimit[availableTokens[j]]; | |
| } | |
| require( | |
| remainingBudget>=_actions[j].value, | |
| remainingBudget[_token]>=_actions[j].value, | |
| string.concat("In ",Strings.toString(j)," action you want to spend more than your limit monthly") | |
| ); | |
| remainingBudget-=_actions[j].value; | |
| remainingBudget[_token]-=_actions[j].value; | |
| } | |
| generatedDAOActions[j]=IDAO.Action(_to, _value, _data); | |
| } | |
| } | |
| /// @notice Checking that can user withdraw this amount | |
| /// @param _actions actions that would be checked | |
| /// @return generatedDAOActions IDAO.Action generated for use in execute | |
| function hasRemainingBudget(WorkingCapitalAction[] calldata _actions) internal returns(IDAO.Action[] memory generatedDAOActions){ | |
| uint _currentMonth = BokkyPooBahsDateTimeLibrary.getMonth(block.timestamp); | |
| uint _currentYear = BokkyPooBahsDateTimeLibrary.getYear(block.timestamp); | |
| generatedDAOActions = new IDAO.Action[](_actions.length); | |
| for (uint j=0; j < _actions.length; j+=1) { | |
| address _to; | |
| uint256 _value; | |
| bytes memory _data; | |
| address _token; | |
| if(_actions[j].erc20Address == address(0)){ | |
| _to=_actions[j].to; | |
| _value=_actions[j].value; | |
| _data= new bytes(0); | |
| _token=address(0); | |
| require(isTokenAvailable(address(0)),"It is not available token in this plugin"); | |
| } | |
| else{ | |
| _to=_actions[j].erc20Address; | |
| _value=0; | |
| bytes memory data = abi.encodeWithSignature("transfer(address,uint256)", _actions[j].to, _actions[j].value); | |
| _data= data; | |
| _token=_actions[j].erc20Address; | |
| require(isTokenAvailable(_actions[j].erc20Address),"It is not available token in this plugin"); | |
| } | |
| // if we are on the month that we were | |
| if(_currentMonth==currentMonth && _currentYear==currentYear){ | |
| require( | |
| remainingBudget[_token]>=_actions[j].value, | |
| string.concat("In ",Strings.toString(j)," action you want to spend more than your limit monthly") | |
| ); | |
| remainingBudget[_token] -=_actions[j].value; | |
| } | |
| // if we are on another month | |
| else{ | |
| currentYear = _currentYear; | |
| currentMonth = _currentMonth; | |
| for (uint j=0; j < availableTokens.length; j+=1) { | |
| remainingBudget[availableTokens[j]]=spendingLimit[availableTokens[j]]; | |
| } | |
| require( | |
| remainingBudget[_token]>=_actions[j].value, | |
| string.concat("In ",Strings.toString(j)," action you want to spend more than your limit monthly") | |
| ); | |
| remainingBudget[_token]-=_actions[j].value; | |
| } | |
| generatedDAOActions[j]=IDAO.Action(_to, _value, _data); | |
| } | |
| } |
There was a problem hiding this comment.
I did it manually
src/WorkingCapital.sol
Outdated
| /// @notice Check that the given token has allowance | ||
| /// @param _token check that given token has allowance | ||
| function isTokenAvailable(address _token) internal view returns (bool) { | ||
| for (uint256 i = 0; i < availableTokens.length; i++) { | ||
| if (availableTokens[i] == _token) { | ||
| return true; | ||
| } | ||
| } | ||
| return false; | ||
| } |
There was a problem hiding this comment.
I think instead of using for you can use hashtable.
Instead of using availableTokens array just use spendingLimit[token].
if spendingLimit[token] was 0 or undefined it means that the token is not available.
mehrdadmms
left a comment
There was a problem hiding this comment.
Please check the suggestions and then test. I didn't test the code just added the suggestions.
change name of struct that store an action Co-authored-by: mehrdad mirmohammadsadeghi <35366864+mehrdadmms@users.noreply.github.com>
In this part has been tried to add another erc20 tokens