Platform: Code4rena
Start Date: 30/05/2023
Pot Size: $300,500 USDC
Total HM: 79
Participants: 101
Period: about 1 month
Judge: Trust
Total Solo HM: 36
Id: 242
League: ETH
Rank: 10/101
Findings: 2
Award: $6,139.29
🌟 Selected for report: 1
🚀 Solo Findings: 1
🌟 Selected for report: yellowBirdy
5707.2337 USDC - $5,707.23
https://github.com/code-423n4/2023-05-maia/blob/54a45beb1428d85999da3f721f923cbf36ee3d35/src/ulysses-omnichain/BranchPort.sol#L158-L169 https://github.com/code-423n4/2023-05-maia/blob/54a45beb1428d85999da3f721f923cbf36ee3d35/src/ulysses-omnichain/BranchPort.sol#L172-L186
BranchPort.manage
allows a registered Strategy to withdraw certain amounts of enabled strategy tokens. It validates access rights ie. if called by a strategy registered for the requested token.
It however doesn't check if the token itself is currently enabled.
Conversely BranchPort.replenishTokens
allows to force withdraw managed tokens from a strategy. It however performs a check if the token is currently an active strategy token.
Strategy token may be disabled by toggleStrategyToken()
even if there are active strategies managing it active. In such case these strategies will still be able to withdraw the tokens with calls to manage()
while replenishTokens
will not be callable on them and thus tokens won't be force returnable.
Pen and Paper.
manage()
getPortStrategyTokenDebt[_strategy][_token] > 0
instead of !isStrategyToken[_token]
in replenishReserves()
Access Control
#0 - c4-judge
2023-07-11T15:11:02Z
trust1995 marked the issue as unsatisfactory: Insufficient proof
#1 - c4-sponsor
2023-07-12T13:33:17Z
0xBugsy marked the issue as sponsor confirmed
#2 - c4-judge
2023-07-25T09:22:51Z
trust1995 marked the issue as satisfactory
#3 - c4-judge
2023-07-25T15:02:17Z
trust1995 marked the issue as primary issue
#4 - c4-judge
2023-07-27T11:02:19Z
trust1995 marked the issue as selected for report
#5 - 0xLightt
2023-09-06T17:21:21Z
🌟 Selected for report: ltyu
Also found by: BPZ, Koolex, RED-LOTUS-REACH, xuwinnie, yellowBirdy
432.0595 USDC - $432.06
https://github.com/code-423n4/2023-05-maia/blob/54a45beb1428d85999da3f721f923cbf36ee3d35/src/ulysses-omnichain/BranchBridgeAgent.sol#L1006-L1011 https://github.com/code-423n4/2023-05-maia/blob/54a45beb1428d85999da3f721f923cbf36ee3d35/src/ulysses-omnichain/lib/AnycallFlags.sol#L11
IAnycallProxy().anyCall()
operates under one of two modes of taking fees, namely fees are taken either on source or on the destination chain. Fee mode is decided by the caller with an appropriate value of the fourth parameter, ie. uint256 _flag
. Values 0,4 denote the SOURCE chain. Such invocation require ether
to be send along (IAnycallProxy().anyCall()
is payable) and in case of insufficient value being sent will fail.
function anyCall( address _to, bytes calldata _data, uint256 _toChainID, uint256 _flags, bytes calldata _extdata ) external payable;
BranchBridgeAgent._performCall()
invokes anyCall()
with AnycallFlags.FLAG_ALLOW_FALLBACK
which is defined as 0x1 << 2
ie 4
source: BranchBridgeAgent.sol
function _performCall(bytes memory _calldata) internal virtual { //Sends message to AnycallProxy IAnycallProxy(localAnyCallAddress).anyCall( rootBridgeAgentAddress, _calldata, rootChainId, AnycallFlags.FLAG_ALLOW_FALLBACK, "" ); }
source: AnycallFlags.sol
uint256 public constant FLAG_ALLOW_FALLBACK = 0x1 << 2;
Consequently all cross bridge requests will fail.
Manual review, Multichain documentation: (Link to anyCall
flags definition)[https://docs.multichain.org/developer-guide/anycall-v7/quickstart-cross-chain-text-example]
Judging from the RootBridgeAgent.anyExecute()
implementation the intended mode is to pay fees on the destination chain (with fallback enabled). Under this assumption _flag = 6
should be passed ie.
AnycallFlags.FLAG_ALLOW_FALLBACK_DST
Other
#0 - c4-judge
2023-07-09T11:58:41Z
trust1995 marked the issue as duplicate of #91
#1 - c4-judge
2023-07-09T11:58:46Z
trust1995 marked the issue as satisfactory