# ERC-20 deposit cctxs are refunded to the EOA instead of an intermediary contract

ZetaChain observer sets inbound cctx Sender to the tx signer EOA rather than msg.sender, so failed ERC-20 deposits refund to the EOA, letting it steal an intermediary contract's escrowed funds.

- Payout: Contest reward
- Severity: medium
- Program: ZetaChain
- Credited: berndartmueller
- Disclosed: 2024-09-06
- Write-up quality: 7/10
- Original: https://github.com/code-423n4/2023-11-zetachain-findings/issues/408
- Library page: https://bugbountylibrary.com/r/c4%3A2023-11-zetachain%3AM-12
- Tags: domain:web3 impact:funds-loss kind:contest platform:code4rena sev:medium tech:evm technique:source-review vuln:access-control vuln:business-logic

**Target:** ZetaChain zetaclient EVM observer / ERC20Custody deposit refund flow
**Root cause:** GetInboundVoteMsgForDepositedEvent recovers the tx signer EOA via signer.Sender(tx) and uses it as the cctx Sender, ignoring msg.sender of ERC20Custody.deposit; refunds on failed cctxs then go to the EOA instead of the calling contract.
**Steps:**
1. Intermediary contract (e.g. Escrow) holds user ERC-20 funds and calls `custody.deposit(recipient, asset, amount, message)` after approving.
2. Observer processes the Deposited event via `GetInboundVoteMsgForDepositedEvent`, setting Sender = `signer.Sender(tx)` (the EOA that signed the tx), not the contract.
3. Cause the inbound cctx to fail, e.g. deposit asset whose liquidity cap is reached, returning `types.ErrForeignCoinCapReached`.
4. Refund is issued to the recorded Sender (the EOA), which receives the intermediary contract's escrowed funds.
**How found:** Code-reading of the observer's event-to-vote-message conversion: noticed Sender is derived from the tx signer rather than the deposit caller, then traced the refund path in keeper_cross_chain_tx_vote_inbound_tx.go to confirm funds go to that Sender.
**Exploitation:** An owner/operator EOA of an intermediary contract bridges aggregated escrowed user funds; by triggering a cctx failure (e.g. liquidity cap reached), the refund is routed to the EOA rather than the contract, letting the EOA abscond with user funds held in escrow.
**Insight:** When a bridge derives a refund beneficiary from tx-level signer recovery instead of the actual calling contract (msg.sender), any contract-mediated deposit misattributes refunds; check event schemas for missing caller field and trace refund routing.
