-
Notifications
You must be signed in to change notification settings - Fork 1
Add erc20 support #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
base: main
Are you sure you want to change the base?
Conversation
added update spending limit functionality
fix bugs about fixed size memory struct
mehrdadmms
left a comment
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.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| /// @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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| /// @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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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 <[email protected]>
In this part has been tried to add another erc20 tokens