Open gpersoon opened 3 years ago
As far as I see, there are two approaches to solving this.
The first one is as above, by approving the corresponding tokens during setRouter
.
The second approach is to call approveForSpender
, before or after the setRouter
call with the correct _spender
and _amount
.
I kinda favour the second approach, since it requires no changes in the codebase. The only issue is that this needs to go through governance. I assume this isn't a big deal as this can be done at the same time that setRouter
governance proposal passes.
If the second approach is chosen, I recommend documenting this somewhere, that approveForSpender
needs to be done alongside setRouter
governance action.
The suggestion of @uN2RVw5q is possible as long as it is documented well, both at the source of setRouter() and also where government actions are documented.
Based on HAECHI audit Axis v3/Smart Contract Security Analysis/Published on : Oct 13, 2021/Version v1.0, NativeStrategyCurve3Crv does not update Crvallowance setting a new router:
Vulnerability details
The Constructor of BaseStrategy.sol sets an allowance for ETH: https://github.com/yaxis-project/metavault/blob/aef0124094ed28945cde2e5b80f453cdbca1e8e5/contracts/v3/strategies/BaseStrategy.sol#L67
setRouter() updates this allowance: https://github.com/yaxis-project/metavault/blob/aef0124094ed28945cde2e5b80f453cdbca1e8e5/contracts/v3/strategies/BaseStrategy.sol#L103-L104
However the Strategy contracts also set allowances for _router, which are not updated with setRouter():
https://github.com/yaxis-project/metavault/blob/aef0124094ed28945cde2e5b80f453cdbca1e8e5/contracts/v3/strategies/NativeStrategyCurve3Crv.sol#L51
https://github.com/yaxis-project/metavault/blob/6c3eac8b45d639339373ea0d6321debf34d5c80c/contracts/v3/strategies/MIMConvexStrategy.sol#L92-L93
https://github.com/yaxis-project/metavault/blob/8f35c3da08c909c2e1879f20d733714ce4a76210/contracts/v3/strategies/GeneralConvexStrategy.sol#L78-L79
https://github.com/yaxis-project/metavault/blob/8f35c3da08c909c2e1879f20d733714ce4a76210/contracts/v3/strategies/ConvexStrategy.sol#L77-L78
Afterthought: reset the allowance for the previous router
Recommended mitigation steps
function setRouter(address _router) virtual external { require(msg.sender == manager.governance(), "!governance"); IERC20(weth).safeApprove(address(router), 0); // reset previous allowance router = ISwap(_router); IERC20(weth).safeApprove(address(_router), 0); IERC20(weth).safeApprove(address(_router), type(uint256).max); }
Implement an override function setRouter() in the following contracts:
For example: function setRouter(address _router) override external { require(msg.sender == manager.governance(), "!governance"); IERC20(_crv).safeApprove(address(router), 0); // reset previous allowance // except in NativeStrategyCurve3Crv.sol IERC20(_cvx).safeApprove(address(router), 0); // reset previous allowance super.setRouter(); IERC20(_crv).safeApprove(address(_router), 0); // check if necessary IERC20(_crv).safeApprove(address(_router), type(uint256).max); IERC20(_cvx).safeApprove(address(_router), 0); // check if necessary IERC20(_cvx).safeApprove(address(_router), type(uint256).max); // except in NativeStrategyCurve3Crv.sol }
Note: verify if setting the allowance to 0 first is important as is done in the current setRouter(); If it is important do it before the safeApprove, also in the constructors, to prevent a griefing attack For WETH is doesn't seem important.