xanoutas/solidity-security-findings huggingface dataset:
Mishandling database errors in BuildBlockIndex() might result in omitting unprocessed blocks
Audit: 2023-11-zetachain-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-11-zetachain/blob/b237708ed5e86f12c4bddabddfd42f001e81941a/repos/node/zetaclient/evm_client.go#L1089
https://github.com/code-423n4/2023-11-zetachain/blob/b237708ed5e86f12c4bddabddfd42f001e81941a/repos/node/zetaclient/bitcoin_client.go#L1220


# Vulnerability details

## Impact
Due to not handling correctly ErrRecordNotFound error in db.First() call a zetaclient might wrongly assume there 
was no previously scanned block number and start from other block making some range of blocks unprocessed, and eventually never processing certain events that have happened.

## Proof of Concept
Any error from database during loading last scanner block number is assumed to be not found error:

```go
	if err := ob.db.First(&lastBlockNum, clienttypes.LastBlockNumID).Error; err != nil {
            logger.Info().Msg("db PosKey does not exist; read from ZetaCore")
            lastheight, err := ob.getLastHeight()
            if err != nil {
                logger.Warn().Err(err).Msg("getLastHeight error")
            }
        ob.SetLastBlockHeightScanned(lastheight)
```

In such case the last block is fetched from zetaclient (by ob.getLastHeight  via zetaClient.GetLastBlockHeightByChain) and persistet in memory via `SetLastBlockHeightScanned`.

In reality, the error from database might be something different than not found error, occuring from different condition i.e. problem with connection to DB, problem with file read under load etc.

Consider following scenario:
- last read block by zetaclient is 1000
- block is saved to database
- zetaclient crashes / restarts
- last scanned block is being read from database, but it results in error
- zetaclient assumes it's not found error
- last block height is read from zetaClient.GetLastBlockHeightByChain call with block number = 1010
- block number = 1010 is stored correctly in database
- ~10 blocks between 1000 and 1010 are omitted from processing by zetaclient which might lead to never processing certain events that have happened.

## Recommended Mitigation Steps

- correctly check that error type from ob.db.First() is gorm's ErrRecordNotFound
- halt zetaclient for further inspection otherwise.





## Assessed type

Other
Lack of minium amount limitation of ZETA received
Audit: 2023-11-zetachain-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-11-zetachain/blob/main/repos/protocol-contracts/contracts/zevm/ZetaConnectorZEVM.sol#L92-L108
https://github.com/code-423n4/2023-11-zetachain/blob/main/repos/protocol-contracts/contracts/evm/ZetaConnector.eth.sol#L31-L45
https://github.com/code-423n4/2023-11-zetachain/blob/main/repos/protocol-contracts/contracts/evm/ZetaConnector.non-eth.sol#L40-L53


# Vulnerability details

## Impact
When users call `send()` function to transfer ZETA tokens cross-chain, they might receive significantly fewer tokens than initially anticipated.

## Proof of Concept
Any user is able to transfer ZETA token between Zeta chain and connected chain and call `destinationAddress#onZetaMessage()` optionally on `destinationChainId` chain:
- Users can call [`ZetaConnectorZEVM#send()`](https://github.com/code-423n4/2023-11-zetachain/blob/main/repos/protocol-contracts/contracts/zevm/ZetaConnectorZEVM.sol#L92-L108) on the Zeta chain to transfer ZETA tokens to `destinationAddress`, and subsequently trigger `destinationAddress#onZetaMessage()` on the `destinationChainId` chain.
- User can call [`ZetaConnectorEth#send()`](https://github.com/code-423n4/2023-11-zetachain/blob/main/repos/protocol-contracts/contracts/evm/ZetaConnector.eth.sol#L31-L45) on the Ethereum to transfer ZETA tokens to `destinationAddress`, and subsequently trigger `destinationAddress#onZetaMessage()` on the `destinationChainId` chain.
- User can call [`ZetaConnectorNonEth#send()`](https://github.com/code-423n4/2023-11-zetachain/blob/main/repos/protocol-contracts/contracts/evm/ZetaConnector.non-eth.sol#L40-L53) on the evm-compitable chain to transfer ZETA tokens to `destinationAddress`, and subsequently trigger `destinationAddress#onZetaMessage()` on the `destinationChainId` chain.
When `send()` is called, A `ZetaSent` event is emitted, the ZetaChain system will forward the message to the destination chain and call `onZetaMessage` at contract address `destinationAddress`.
- If the `destinationChainId` of `send()` is Ethereum chain, [`ZetaConnectorEth#onReceive()`](https://github.com/code-423n4/2023-11-zetachain/blob/main/repos/protocol-contracts/contracts/evm/ZetaConnector.eth.sol#L52-L70) will be invoked, `zetaValue` of ZETA token will be transferred to `destinationAddress`.
- Otherwize, [`ZetaConnectorNonEth#onReceive()`](https://github.com/code-423n4/2023-11-zetachain/blob/main/repos/protocol-contracts/contracts/evm/ZetaConnector.non-eth.sol#L61-L79) will be invoked on the `destinationChainId` chain, `zetaValue` of ZETA token will be minted to `destinationAddress`

However, the initial callers of `send()` have no way to know the quantity of ZETA tokens the `destinationAddress` will receive until the transaction is successfully executed on the destination chain, since `zetaValue` is not specified by the caller.

As described in [`ZetaConnectorZEVM#L17`](https://github.com/code-423n4/2023-11-zetachain/blob/main/repos/protocol-contracts/contracts/zevm/ZetaCo
Denial of Service By Increasing Price Of Gas Tokens in Uniswapv2 Pool
Audit: 2023-11-zetachain-findings
Severity: HIGH

# Lines of code

https://github.com/zeta-chain/node/blob/b98f760fb2c6ef13aea64db15172ee1af6c4ca02/x/crosschain/keeper/gas_payment.go#L22


# Vulnerability details

## Impact
Zetachain allows users to pay gas fees in three different ways. Two of these methods require swapping attached ZETA for the native gas token of the underlying chain, which is done via a Uniswapv2 pool that contains WZETA and the gasZRC20. For a given CCTX to be completed, these swaps must succeed within the bounds of gas limits and the amount of tokens sent in with the transaction. However, Because these pools are created with a very small amount of liquidity, and are not likely to become much larger, an attacker can performs swaps against these pools to prevent transactions from or to any given chain from operating, by simply swapping a small amount of ZETA against that chain's respective gas token.

In the worst case, this is a full denial of service since all CCTXs won't have enough ZetaBurnt to pay for gas operations, and will just revert. Best case, this skyrockets the gas price for operating on any chain via Zetachain, which would prevent most operations from ever being profitable, and thus in a round about way create a denial of service.

Additionally, because the gas amount is sent into the gas payment system directly from the CCTX, it is possible that a sending user could lose a significant amount of their money sent in if they did not anticipate gas prices being as high as they are. The `amount` on the CCTXs are sent directly with the gas price calculations and payments, which means that a user sending in `amount1` of ZETA, and expecting a given gas price, could end up paying a significantly higher price for their transaction, which would then also end up subtracting a much higher amount of their ZETA, which would then be burned, without any recourse from the user. So, to summarize, this could also lead to great amounts of unintentionally lost funds.

This is a straightforward exploit that could be easily carried out for a relatively small amount of ZETA or ZRC20. It could be easily be counteracted by simply swapping more tokens back INTO the pool, however, the more tokens that get put into these gas-swapping pools to keep liquidity high in order to prevent potential denial of service, the more that significant arbitrage opportunities arise on these otherwise only infrastructure-necessary pools, which could also cause gas prices to become high and extremely different from chain to chain.

## Proof of Concept
Copy the following code into the ``x/crosschain/keeper/gas_payment_test.go`` file and run it with the following command: 

``go test -v . -run TestKeeperGing3r_DrainLiquidityPools``

```
func drainUniswapPool(t *testing.T, ctx sdk.Context, sdkKeeper *testkeeper.SDKKeepers, fk *testkeeper.ZetaKeepers, zrc20 ethcommon.Address) {
	//our swap to drain the pool, which should cause a denial of service.
	//just create a random Eth address to send the swapped tokens to
`onReceive()` in `ZetaConnector.eth.sol` and `ZetaConnector.non-eth.sol` can be called even when contract is paused
Audit: 2023-11-zetachain-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-11-zetachain/blob/b237708ed5e86f12c4bddabddfd42f001e81941a/repos/protocol-contracts/contracts/evm/ZetaConnector.eth.sol#L52
https://github.com/code-423n4/2023-11-zetachain/blob/b237708ed5e86f12c4bddabddfd42f001e81941a/repos/protocol-contracts/contracts/evm/ZetaConnector.non-eth.sol#L61


# Vulnerability details

## Impact

`onReceive()` can be called even when the contract is in a paused state.
Paused state suggests that there shouldn't be any actions nor changes on the current, paused contract. 
When we verify the whole implementation of `ZetaConnector.eth.sol` and `ZetaConnector.non-eth.sol`, we can spot, that every function `send()` and `onRevert()` implements `whenNotPaused` modifier. The `whenNotPaused` modifier is only missing in `onReceive()` which implies, that even when the contract is paused - `onReceive()` can be called.


## Proof of Concept

`onReceive()` is a handler to receive data from other chain. According to its implementation, it's responsible to transfer the Zeta tokens to destination and calls onZetaMessage:

```
  bool success = IERC20(zetaToken).transfer(destinationAddress, zetaValue);
  [...]
  ZetaReceiver(destinationAddress).onZetaMessage(
```

It should not be possible to perform those actions when contract is in a paused state. However, `onReceive()` does not implement any check which verifies if contract is paused. Function `onReceive()` misses `whenNotPaused` modifier - which implies that `onReceive()` can be called even when contract is paused.

Since every other function in `ZetaConnector.eth.sol` implements `whenNotPaused` - it's reasonable to assume that this modifier is missing in `onReceive()`.

The very same issue occurs in `ZetaConnector.non-eth.sol` - which, similarly to `ZetaConnector.eth.sol` - does not implement `whenNotPaused` on `onReceive()` function.


## Tools Used

Manual code review

## Recommended Mitigation Steps

Implement `whenNotPaused` modifier in `onReceive()`


## Assessed type

Access Control
Forged `ZetaSent` events allow minting an infinite amount of Zeta tokens on ZetaChain and impersonating other dApps
Audit: 2023-11-zetachain-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-11-zetachain/blob/b237708ed5e86f12c4bddabddfd42f001e81941a/repos/node/zetaclient/inbound_tracker.go#L157


# Vulnerability details

## Impact

Arbitrary minting of Zeta tokens on ZetaChain. 

Additionally, such forged `ZetaSent` events allow impersonating and faking other legitimate cross-chain messages from other dApps.

## Proof of Concept

The [`CheckReceiptForCoinTypeZeta`](https://github.com/code-423n4/2023-11-zetachain/blob/b237708ed5e86f12c4bddabddfd42f001e81941a/repos/node/zetaclient/inbound_tracker.go#L144-L177) function, part of the observer's EVM inbound tracker, is used to confirm an EVM transaction that was originally sent to the connector contract on an external EVM chain, e.g., [sending a cross-chain message](https://github.com/code-423n4/2023-11-zetachain/blob/b237708ed5e86f12c4bddabddfd42f001e81941a/repos/protocol-contracts/contracts/evm/ZetaConnector.eth.sol#L31-L45), or having [received a cross-chain message](https://github.com/code-423n4/2023-11-zetachain/blob/b237708ed5e86f12c4bddabddfd42f001e81941a/repos/protocol-contracts/contracts/evm/ZetaConnector.eth.sol#L52-L70).

In the latter case, [calling the `onZetaMessage` on the receiver `destinationAddress` address](https://github.com/code-423n4/2023-11-zetachain/blob/b237708ed5e86f12c4bddabddfd42f001e81941a/repos/protocol-contracts/contracts/evm/ZetaConnector.eth.sol#L64-L66) can result in further cross-chain messages sent via the connector's `send` function. Thus, the `CheckReceiptForCoinTypeZeta` function processes the transaction receipt's logs to find such a relevant `ZetaSent` event, which is then used to construct a `MsgVoteOnObservedInboundTx` message that is sent to the ZetaChain in line `169`.

```go
144: func (ob *EVMChainClient) CheckReceiptForCoinTypeZeta(txHash string, vote bool) (string, error) {
145: 	connector, err := ob.GetConnectorContract()
146: 	if err != nil {
147: 		return "", err
148: 	}
149: 	hash := ethcommon.HexToHash(txHash)
150: 	receipt, err := ob.evmClient.TransactionReceipt(context.Background(), hash)
151: 	if err != nil {
152: 		return "", err
153: 	}
154:
155: 	var msg types.MsgVoteOnObservedInboundTx
156: 	for _, log := range receipt.Logs {
157: ❌	event, err := connector.ParseZetaSent(*log)
158: 		if err == nil && event != nil {
159: 			msg, err = ob.GetInboundVoteMsgForZetaSentEvent(event)
160: 			if err == nil {
161: 				break
162: 			}
163: 		}
164: 	}
165: 	if !vote {
166: 		return msg.Digest(), nil
167: 	}
168:
169: 	zetaHash, err := ob.zetaClient.PostSend(PostSendNonEVMGasLimit, &msg)
... 	// [...]
177: }
```

However, the `ParseZetaSent` function in line `157` **does not check the emitting contract** of the event.

Consequently, an attacker can fake a `ZetaSent` event from within the called `onZetaMessage` function and [add this transaction's hash to ZetaChain's inbound transaction tracker](https://github.com/code-423n4/2023-11-zetachain/blob/b237708ed5e86f12c4bddabddfd42f001e81941a/rep
Failures of inbound transactions will lead to deposited funds loss and other failures
Audit: 2023-11-zetachain-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-11-zetachain/blob/0df9a114103b6ed7536d65a03e2e4876eb8bdbc0/repos/node/zetaclient/tx.go#L127-L142
https://github.com/code-423n4/2023-11-zetachain/blob/0df9a114103b6ed7536d65a03e2e4876eb8bdbc0/repos/node/zetaclient/bitcoin_client.go#L380
https://github.com/code-423n4/2023-11-zetachain/blob/0df9a114103b6ed7536d65a03e2e4876eb8bdbc0/repos/node/zetaclient/evm_client.go#L856
https://github.com/code-423n4/2023-11-zetachain/blob/0df9a114103b6ed7536d65a03e2e4876eb8bdbc0/repos/node/zetaclient/evm_client.go#L898
https://github.com/code-423n4/2023-11-zetachain/blob/0df9a114103b6ed7536d65a03e2e4876eb8bdbc0/repos/node/zetaclient/evm_client.go#L977
https://github.com/code-423n4/2023-11-zetachain/blob/0df9a114103b6ed7536d65a03e2e4876eb8bdbc0/repos/node/zetaclient/inbound_tracker.go#L104
https://github.com/code-423n4/2023-11-zetachain/blob/0df9a114103b6ed7536d65a03e2e4876eb8bdbc0/repos/node/zetaclient/inbound_tracker.go#L169
https://github.com/code-423n4/2023-11-zetachain/blob/0df9a114103b6ed7536d65a03e2e4876eb8bdbc0/repos/node/zetaclient/inbound_tracker.go#L203
https://github.com/code-423n4/2023-11-zetachain/blob/0df9a114103b6ed7536d65a03e2e4876eb8bdbc0/repos/node/zetaclient/inbound_tracker.go#L252


# Vulnerability details

## Impact

In multiple places throughout the ZetaClient code, the pattern similar to [the following one](https://github.com/code-423n4/2023-11-zetachain/blob/0df9a114103b6ed7536d65a03e2e4876eb8bdbc0/repos/node/zetaclient/evm_client.go#L898-L903) is observed:
```go
zetaHash, err := ob.zetaClient.PostSend(PostSendEVMGasLimit, &msg)
if err != nil {
    ob.logger.ExternalChainWatcher.Error().Err(err).Msg("error posting to zeta core")
    return
}
```
The code above occurs in the [task 2: Query evm chain for deposited logs](https://github.com/code-423n4/2023-11-zetachain/blob/0df9a114103b6ed7536d65a03e2e4876eb8bdbc0/repos/node/zetaclient/evm_client.go#L865-L905) of function [observeInTX()](https://github.com/code-423n4/2023-11-zetachain/blob/0df9a114103b6ed7536d65a03e2e4876eb8bdbc0/repos/node/zetaclient/evm_client.go#L774), which repeatedly scans EVM logs for the instances of `Deposited` events, generated when a user deposits fund to ZetaChain. 

The code of [PostSend()](https://github.com/code-423n4/2023-11-zetachain/blob/0df9a114103b6ed7536d65a03e2e4876eb8bdbc0/repos/node/zetaclient/tx.go#L127-L142) is as follows:

```go
func (b *ZetaCoreBridge) PostSend(zetaGasLimit uint64, msg *types.MsgVoteOnObservedInboundTx) (string, error) {
	authzMsg, authzSigner, err := b.WrapMessageWithAuthz(msg)
	if err != nil {
		return "", err
	}

	for i := 0; i < DefaultRetryCount; i++ {
		zetaTxHash, err := b.Broadcast(zetaGasLimit, authzMsg, authzSigner)
		if err == nil {
			return zetaTxHash, nil
		}
		b.logger.Debug().Err(err).Msgf("PostSend broadcast fail | Retry count : %d", i+1)
		time.Sleep(DefaultRetryInterval * time.Second)
	}
	return "", fmt.Errorf("post send failed after %d retries", DefaultRetryInterva
Forcing Omnichain calls to fire `onCrossChainCall` if the receiver is a smart contract address, prevents building some applications on the network.
Audit: 2023-11-zetachain-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-11-zetachain/blob/main/repos/node/x/fungible/keeper/deposits.go#L69-L77


# Vulnerability details


## Impact

When making a deposit method `ERC20Custody::deposit()` [[1](https://github.com/code-423n4/2023-11-zetachain/blob/main/repos/protocol-contracts/contracts/evm/ERC20Custody.sol#L165-L185)], or sending native coins to the `TSS_ADDRESS` by making the receiver address a contract address on ZetaChain. ZetaChain will fire `fungible:keeper:DepositZRC20AndCallContract()`[[2](https://github.com/code-423n4/2023-11-zetachain/blob/main/repos/node/x/fungible/keeper/deposits.go#L75)], and if it is an EOA address it will fire `fungible:keeper:DepositZRC20` [[3](https://github.com/code-423n4/2023-11-zetachain/blob/main/repos/node/x/fungible/keeper/deposits.go#L84)].


[node/x/fungible/keeper/deposits.go#L65-L85C24](https://github.com/code-423n4/2023-11-zetachain/blob/main/repos/node/x/fungible/keeper/deposits.go#L65-L85C24)
```go
    // check if the receiver is a contract
    // if it is, then the hook onCrossChainCall() will be called
    // if not, the zrc20 are simply transferred to the receiver
    acc := k.evmKeeper.GetAccount(ctx, to)
    if acc != nil && acc.IsContract() {
        context := systemcontract.ZContext{
            Origin:  from,
            Sender:  eth.Address{},
            ChainID: big.NewInt(senderChain.ChainId),
        }
        res, err := k.DepositZRC20AndCallContract(ctx, context, ZRC20Contract, to, amount, data)
        return res, true, err
    }
    
    // if the account is a EOC, no contract call can be made with the data
    if len(data) > 0 {
        return nil, false, types.ErrCallNonContract
    }
    
    res, err := k.DepositZRC20(ctx, ZRC20Contract, to, amount)
    return res, false, err
```

In `DepositZRC20AndCallContract()`, the Observers are firing `zevm/SystemContract::depositAndCall()` [[4](https://github.com/code-423n4/2023-11-zetachain/blob/main/repos/node/x/fungible/keeper/evm.go#L288-L303)] which fires `onCrossChainCall()` on the target (receiver) address on the zetachain [[5](https://github.com/code-423n4/2023-11-zetachain/blob/main/repos/protocol-contracts/contracts/zevm/SystemContract.sol#L67-L79)].

```solidity
// FILE: zevm/SystemContract::depositAndCall()
    function depositAndCall( ... ) external {
        if (msg.sender != FUNGIBLE_MODULE_ADDRESS) revert CallerIsNotFungibleModule();
        if (target == FUNGIBLE_MODULE_ADDRESS || target == address(this)) revert InvalidTarget();

        IZRC20(zrc20).deposit(target, amount);
        zContract(target).onCrossChainCall(context, zrc20, amount, message); // crossChain call
    }
```

So if the receiver address is a smart contract and is not implementing `onCrossChainCall()` function, it will not be able to receive money.

This logic is not good, as it forces the receiving address to have `onCrossChainCall` to receive funds.

Some applications that can be built on Zetachain that receive Omnichain f
Zeta Observer nodes are not listening to `internal TXs`, which makes Smart Contract Wallets users' funds locked when making `Omnichain calls`.
Audit: 2023-11-zetachain-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-11-zetachain/blob/main/repos/node/zetaclient/evm_client.go#L952


# Vulnerability details


## Impact

Zetachain allows omnichain TXs (sending funds from external chains to Zeta EVM chain), using different methods.
- If it's just sending main blockchain coin funds between addresses: You deposit funds directly to the `TSS_ADDRESS` and it will send money to the destination address as ZRC-20 tokens on Zeta EVM chain.
- If you are sending ERC20: You need to use `ERC20Custody::deposit()`.

The problem occurs in the first sending method. When the user sends funds (native chain coins) to the `TSS_ADDRESS`.

To define the receiver address you have two things to do:
- provide it in the data field of the TX in bytes format (+ any additional message if needed), and Observer nodes take out the rest of the job to decode.
- Not providing data field in the TX, and in that case, Observers use the caller itself as the receiver address on the Zeta blockchain.

The problem affects Omnichain (Inbound TXs, external EVM => Zeta EVM) which is made by smart contract wallet users.

When smart contract wallets make a sending request, it doesn't make an RPC call. They are making low-level `call` to transfer funds. And here is where the problem occurs.

Observers will not notice this transaction, funds will be sent to the `TSS_ADDRESS` and the Observers will not know that there is an Omnichain call has happened. So user funds will be locked in the `TSS_ADDRESS`.

**Why Smart Contract Wallets are important?**
We should keep in mind that Smart Contract wallets are heavily adopted, and The problem does not affect a small group of users. Mult-sig wallets are increasing, and Account Abstraction is coming, Users will deal with Smart Contract Wallets in the near future instead of EOA.

This is dangerous for users, as most of the users are using their wallets using a UI, and some popular wallets like MetaMask will support account abstraction soon, which will make users' wallets a Smart Contract Wallet as we illustrated before. So users will not be able to interact with ZetaChain and will lose their funds without knowing what is going wrong.


## Proof of Concept

> In our auditing process of the protocol, we made a lot of things (installing deps, making contracts, writing deployments and interact scripts, etc...), So It will be hard for the judger to set up the development environment.
> We worked on the second part `protocol-contract`, and used zeta_testnet for our testing purposes.
> Here is the Dropbox link to download the `protocol-contract` we worked on, you will find `setup.md` to help you install deps, and run POC scripts easily without any problems.

Downloading link: https://www.dropbox.com/scl/fo/b078exeuugkyb72tcs7a0/h?rlkey=mzi4pjja4cbbnyc58yz6e3fns&dl=0 

To not make the POC page too long, we will mention the main points we made to prove the problem. And In the Dropbox folder, everything including (testing script, con