xanoutas/solidity-security-findings huggingface dataset:
Predictable Contract Deployments Enable Front-Running Attacks
Audit: 2023-11-zetachain-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-11-zetachain/blob/main/repos/protocol-contracts/contracts/evm/tools/ImmutableCreate2Factory.sol#L26-L74
https://github.com/code-423n4/2023-11-zetachain/blob/main/repos/protocol-contracts/contracts/evm/tools/ImmutableCreate2Factory.sol#L148-L173


# Vulnerability details

## Impact
The CREATE2 address calculation in the ImmutableCreate2Factory contract deterministically derives deployment addresses from the salt, caller, and init code hash alone. This lack of randomness allows third-parties to easily predict future contract addresses from pending transactions and deploy contracts preemptively - known as front-running. Front-runners could hijack intended contract ownership and functionality.


## Proof of Concept
The safeCreate2Internal() and findCreate2Address() functions calculate the deployment address without incorporating additional randomness. By watching for transactions with a given salt/code hash, an attacker could call CREATE2 first to deploy a contract to the expected address before the legitimate deployment.

```solidity
    function safeCreate2Internal(
        bytes32 salt,
        bytes memory initializationCode
    ) internal returns (address deploymentAddress) {
        // move the initialization code from calldata to memory.
        bytes memory initCode = initializationCode;

        // determine the target address for contract deployment.
        address targetDeploymentAddress = address(
            uint160( // downcast to match the address type.
                uint256( // convert to uint to truncate upper digits.
                    keccak256( // compute the CREATE2 hash using 4 inputs.
                        abi.encodePacked( // pack all inputs to the hash together.
                            hex"ff", // start with 0xff to distinguish from RLP.
                            address(this), // this contract will be the caller.
                            salt, // pass in the supplied salt value.
                            keccak256(abi.encodePacked(initCode)) // pass in the hash of initialization code.
                        )
                    )
                )
            )
        );

        // ensure that a contract hasn't been previously deployed to target address.
        require(!_deployed[targetDeploymentAddress], "Invalid contract creation - contract has already been deployed.");

        // using inline assembly: load data and length of data, then call CREATE2.
        assembly {
            // solhint-disable-line
            let encoded_data := add(0x20, initCode) // load initialization code.
            let encoded_size := mload(initCode) // load the init code's length.
            deploymentAddress := create2(
                // call CREATE2 with 4 arguments.
                callvalue, // forward any attached value.
                encoded_data, // pass in initialization code.
                encoded_size, // pass in init code's length.
                sa
Gas transfers to TSS address may not be not recorded 
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#L943-L986


# Vulnerability details

Whenever a new block on observed chain is considered finalized, zetaclient scraps all relevant transactions sending assets to its contracts. In case of native (gas) assets, it queries the incoming tx to TSS address in [zetaclient/evm_client](https://github.com/code-423n4/2023-11-zetachain/blob/b237708ed5e86f12c4bddabddfd42f001e81941a/repos/node/zetaclient/evm_client.go#L907-L993):
```go
	// task 3: query the incoming tx to TSS address ==============
	func() {
		tssAddress := ob.Tss.EVMAddress() // after keygen, ob.Tss.pubkey will be updated
		// [...]

		// query incoming gas asset
		for bn := startBlock; bn <= toBlock; bn++ {
			// [...]
			for _, tx := range block.Transactions() {
				if tx.To() == nil {
					continue
				}
				if bytes.Compare(tx.Data(), []byte(DonationMessage)) == 0 {
					ob.logger.ExternalChainWatcher.Info().Msgf("thank you rich folk for your donation!: %s", tx.Hash().Hex())
					continue
				}

				if *tx.To() == tssAddress { // @audit if transaction uses intermediary contract and then tssAddress it won't be counted
					receipt, err := ob.evmClient.TransactionReceipt(context.Background(), tx.Hash())
					if err != nil {
						ob.logger.ExternalChainWatcher.Err(err).Msg("TransactionReceipt error")
						continue
					}
					if receipt.Status != 1 { // 1: successful, 0: failed
						ob.logger.ExternalChainWatcher.Info().Msgf("tx %s failed; don't act", tx.Hash())
						continue
					}
					// [...]
```

The problem with this approach however is described at the *@audit* annotation above - it checks for funds sent to TSS address by comparing `tx.To` to it. There are however at least 2 cases when this check will fail. One of the cases where it just won't work is Account Abstraction, where multiple transactions are gathered in a bundle and sent to the entrypoint contract, so the entrypoint contract is `tx.To` in such case. Second issue is using intermediary contract, which can send transactions to TSS, which also won't be recorded. There also is an additional error, because in such case transaction sender is set to transaction sender, which in case of Account Abstraction or any smart contract wallet will not the the address of the sender:

```go
					from, err := ob.evmClient.TransactionSender(context.Background(), tx, block.Hash(), receipt.TransactionIndex) // @audit transaction sender is bundler in case of Account Abstraction.
					if err != nil {
						ob.logger.ExternalChainWatcher.Err(err).Msg("TransactionSender error; trying local recovery (assuming LondonSigner dynamic fee tx type) of sender address")
						signer := ethtypes.NewLondonSigner(big.NewInt(ob.chain.ChainId))
						from, err = signer.Sender(tx)
						if err != nil {
							ob.logger.ExternalChainWatcher.Err(err).Msg("local recovery of sender address failed")
							continue
Zetaclient should cointinue instead of return in  send/deposit logs handling tasks
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#L859
https://github.com/code-423n4/2023-11-zetachain/blob/main/repos/node/zetaclient/evm_client.go#L901


# Vulnerability details

## Impact
When a message fails during `PostSend` process, the left logs won't be handled. This can lead to users' assets loss when other clients also fail or split chain if other clients success.

## Proof of Concept
In `zetaclient/evm_client.go:observeInTX()`, the client will handle the observed sent/deposit/TSS events from external chains. Especially, it trigger three tasks/functions to filter and handles the logs from those events. Give the sent task as an example:

```solidity
	//task 1:  Query evm chain for zeta sent logs
	func() {
		// ...
		logs, err := connector.FilterZetaSent(&bind.FilterOpts{
			// #nosec G701 always positive
			Start:   uint64(startBlock),
			End:     &tb,
			Context: context.TODO(),
		}, []ethcommon.Address{}, []*big.Int{})
		if err != nil {
			ob.logger.ChainLogger.Warn().Err(err).Msgf("observeInTx: FilterZetaSent error:")
			return
		}
		// Pull out arguments from logs
		for logs.Next() {
			msg, err := ob.GetInboundVoteMsgForZetaSentEvent(logs.Event)
			if err != nil {
				ob.logger.ExternalChainWatcher.Error().Err(err).Msg("error getting inbound vote msg")
				continue // @audit there are logs/msgs left to be handled
			}

			zetaHash, err := ob.zetaClient.PostSend(PostSendNonEVMGasLimit, &msg)
			if err != nil {
				ob.logger.ExternalChainWatcher.Error().Err(err).Msg("error posting to zeta core")
				return // @audit should be continue
			}
			ob.logger.ExternalChainWatcher.Info().Msgf("ZetaSent event detected and reported: PostSend zeta tx: %s", zetaHash)
		}
	}()
```

As we can see, the logs are get from `connector.FilterZetaSent` and then used in a loop to get the message in them and post the message to zetacore. There are two things we need to pay attention in the last loop:

1. If we failed to get the message from logs (e.g. the sent destination address is the ZETA token contract), we will log the error and **continue** to handle the left logs/messages.
2. If we failed to postsend the message to zetacore (e.g. in broadcast, the account sequence mismatch when network is busy), we will **return** from the function which abort the task.

So the problem is (also in `task 2: Query evm chain for deposited logs`.), if there is a message fail to be sent to zetacore, all the left sent events won't be handled. Even worse, after we finished all the tasks in `observeInTX()`, the scanned blocks `toBlock` will be set for next call of `observerInTX()`, so the unhandled events will never be handled again, leading to assets loss:

```solidity
	// ============= end of query the incoming tx to TSS address ==============
	ob.SetLastBlockHeightScanned(toBlock) // <===
	if err := ob.db.Save(clienttypes.ToLastBlockSQLType(ob.GetLastBlockHeightScanned())).Error; err != nil {
		ob.logger.Externa
`ZetaConnectorEth.onReceive()` FUNCTION CAN BE CALLED WHEN THE CONTRACT IS PAUSED THUS BREAKING THE INTENDED BEHAVIOUR OF THE PROTOCOL
Audit: 2023-11-zetachain-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-11-zetachain/blob/main/repos/protocol-contracts/contracts/evm/ZetaConnector.eth.sol#L31
https://github.com/code-423n4/2023-11-zetachain/blob/main/repos/protocol-contracts/contracts/evm/ZetaConnector.eth.sol#L77-L85
https://github.com/code-423n4/2023-11-zetachain/blob/main/repos/protocol-contracts/contracts/evm/ZetaConnector.eth.sol#L52-L59
https://github.com/code-423n4/2023-11-zetachain/blob/main/repos/protocol-contracts/contracts/evm/ZetaConnector.non-eth.sol#L61-L68


# Vulnerability details

## Impact

The `ZetaConnector.eth.sol` contract is the  ETH implementation of ZetaConnector. This contract has three main functions to manage interactions between TSS and different chains. These functions are: `send()`, `onReceive()` and `onRevert()`. 

Both the `send()` and `onRevert()` functions do not send data to other chains or receive errors from other chains when the contract is paused since they implement the `whenNotPaused` modifier.

But the `whenNotPaused` modifier is not implemented in the `onReceive()` function thus enabling the `zetaConnectorEth` contract to receive data from other chains when the contract is paused. This is not the intended behaviour. since when the contract is puased (Due to an emergency such as an attack) it is recommended to not to continue with important transactions such as `onReceive()` which handles transfer of assets, since it could be exploited by an attacker.

Same issue is prevalent in the `ZetaConnector.non-eth.onReceive` function.

## Proof of Concept

```solidity
    function send(ZetaInterfaces.SendInput calldata input) external override whenNotPaused {
```

https://github.com/code-423n4/2023-11-zetachain/blob/main/repos/protocol-contracts/contracts/evm/ZetaConnector.eth.sol#L31

```solidity
    function onRevert(
        address zetaTxSenderAddress,
        uint256 sourceChainId,
        bytes calldata destinationAddress,
        uint256 destinationChainId,
        uint256 remainingZetaValue,
        bytes calldata message,
        bytes32 internalSendHash
    ) external override whenNotPaused onlyTssAddress {
```

https://github.com/code-423n4/2023-11-zetachain/blob/main/repos/protocol-contracts/contracts/evm/ZetaConnector.eth.sol#L77-L85

```solidity
    function onReceive(
        bytes calldata zetaTxSenderAddress,
        uint256 sourceChainId,
        address destinationAddress,
        uint256 zetaValue,
        bytes calldata message,
        bytes32 internalSendHash
    ) external override onlyTssAddress {
```

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

```solidity
    function onReceive(
        bytes calldata zetaTxSenderAddress,
        uint256 sourceChainId,
        address destinationAddress,
        uint256 zetaValue,
        bytes calldata message,
        bytes32 internalSendHash
    ) external override onlyTssAddress {
```

https://github.com/code-423n4/202
THE `GetDurationFactor` FUNCTION RETURNS AN ERRORNEOUS VALUE FOR THE `duration factor` DUE TO WRONG ASSUMPTION ON NUMBER OF SECONDS PER MONTH
Audit: 2023-11-zetachain-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-11-zetachain/blob/main/repos/node/x/emissions/keeper/block_rewards_components.go#L38-L56
https://github.com/code-423n4/2023-11-zetachain/blob/main/repos/node/x/emissions/types/keys.go#L26


# Vulnerability details

## Impact

The `x/emissions/keeper/block_rewards_components.go` is used to calculate the block reward components such as `reserves factor`, `bond factor` and `duration factor`. 

The `duration factor` is calculated as shown below:

```go
func (k Keeper) GetDurationFactor(ctx sdk.Context) sdk.Dec {
	avgBlockTime := sdk.MustNewDecFromStr(k.GetParams(ctx).AvgBlockTime)
	NumberOfBlocksInAMonth := sdk.NewDec(types.SecsInMonth).Quo(avgBlockTime)
	monthFactor := sdk.NewDec(ctx.BlockHeight()).Quo(NumberOfBlocksInAMonth)
	logValueDec := sdk.MustNewDecFromStr(k.GetParams(ctx).DurationFactorConstant)
	// month * log(1 + 0.02 / 12)
	fractionNumerator := monthFactor.Mul(logValueDec)
	// (month * log(1 + 0.02 / 12) ) + 1
	fractionDenominator := fractionNumerator.Add(sdk.OneDec())

	// (month * log(1 + 0.02 / 12)) / (month * log(1 + 0.02 / 12) ) + 1
	if fractionDenominator.IsZero() {
		return sdk.OneDec()
	}
	if fractionNumerator.IsZero() {
		return sdk.ZeroDec()
	}
	return fractionNumerator.Quo(fractionDenominator)
}
```

The `monthFactor` which is used for both the `fractionNumerator` and `fractionDenominator` calculation, is calculated by dividing the block height by the `NumberOfBlocksInAMonth`. The `NumberOfBlocksInAMonth` is calculated by dividing the `types.SecsInMonth` by the `avgBlockTime` of the blockchain.

The issue here is the value of `types.SecsInMonth` is a constant value which is set to `30 * 24 * 60 * 60`. This assumes that every month in the year consists of 30 days and which is not the case since February is shorter with `28 days` and some months are longer with `31 days (Eg: January)`. Hence the `NumberOfBlocksInAMonth` calculated by assuming `30 days` per month provides errorneous value which propogates to the calculation of the `duration factor`.

For example let's consider the followng two scenarios:

1. Scenario 1:

Number of seconds per month as calculated via `types.SecsInMonth`:
    = 30 * 24 * 60 * 60
    = 2,592,000

Let's assume that `avgBlockTime = 6 seconds` since this is the average block time of cosmos.
Hence the NumberOfBlocksInAMonth = 2,592,000 / 6 = 432000

Current block height of cosmos `18,332,985` is considered as an example to show the discrepency between `monthFactor` calculation. 

Hence the `monthFactor` = 18,332,985 / 432000 = 42.4374652778

2. Scenario 2:

Now let's consider the total number of seconds per year (by considering the shorter months and longer months) and divide the value by 12 to get an average number of seconds per month.

Total seconds per year considering shorter and longer months = 31,536,000
Average number of seconds per month = 31,536,000 / 12 = 2,628,000

Let's assume that `avgBlockTime = 6 seconds` since this is the average block
Malicious users can revert any deposit transaction by swap tokens
Audit: 2023-11-zetachain-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-11-zetachain/blob/b237708ed5e86f12c4bddabddfd42f001e81941a/repos/node/x/crosschain/keeper/gas_payment.go#L164


# Vulnerability details

## Impact

When zeta chain use ERC20 token to pay gas fee, it first calculates the necessary ERC20 token amounts by [uniswapv2#getAmountsIn](https://github.com/code-423n4/2023-11-zetachain/blob/b237708ed5e86f12c4bddabddfd42f001e81941a/repos/node/x/fungible/keeper/system_contract.go#L574-L612) function,

then call [uniswapv2#swapExactTokensForTokens](https://github.com/code-423n4/2023-11-zetachain/blob/b237708ed5e86f12c4bddabddfd42f001e81941a/repos/node/x/crosschain/keeper/gas_payment.go#L208-L225) to get `gasObtained` then compare with `outTxGasFee`.

Malicious users could swap inZRC4 to outZRC4 through wzeta to increase the price of outZRC4, then the `gasObtained` will be less than `outTxGasFee` and the transaction will be reverted.

Besides, the deadline of [uniswapv2#swapExactTokensForTokens](https://github.com/code-423n4/2023-11-zetachain/blob/b237708ed5e86f12c4bddabddfd42f001e81941a/repos/node/x/crosschain/keeper/gas_payment.go#L208-L225) is set to `1e17`,
which means the transaction maybe pending in the memory pool forever.

## Proof of Concept

1. Innocent users bridge tokens and use ERC20 to pas gas fee.
2. Malicious user monitors the memory pool, once `uniswapv2#swapExactTokensForTokens` noticed, he front-run and swap inZRC4 to outZRC4 through wzeta to increase the price of outZRC4.
3. The node will revert the transaction because `gasObtained` is less than `outTxGasFee`.

## Tools Used

vscode

## Recommended Mitigation Steps

Use TWAP mechanism to calculate the necessary ERC20 token amounts, and set the deadline to block.timestamp. 






## Assessed type

Uniswap
Unmatched integer type may overflow and cause node crash
Audit: 2023-11-zetachain-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-11-zetachain/blob/b237708ed5e86f12c4bddabddfd42f001e81941a/repos/node/x/crosschain/keeper/gas_payment.go#L36
https://github.com/code-423n4/2023-11-zetachain/blob/b237708ed5e86f12c4bddabddfd42f001e81941a/repos/node/x/crosschain/keeper/gas_payment.go#L131


# Vulnerability details


## Impact

ZetaChain support to pay gas fee with zeta token, native token and ERC20 token. But when pay gas fee with ERC20 token, the node use unmatched integer type to receive the `inputAmount math.Uint`, because ERC20 amounts range from 0 to 2^256-1, but the `math.Uint` range is [0, 2^64-1], so it may cause node crash.

Node will crash if node use ERC20 amounts larger than 2^64-1 to pas gas fee, although the `math.Uint` max value is `2^256-1` marked by `go/pkg/mod/cosmossdk.io/math@v1.0.0-rc.0/uint.go`, the test case shows that the `math.Uint` max value is `2^64-1`.


## Proof of Concept

Change the test case in `https://github.com/code-423n4/2023-11-zetachain/blob/main/repos/node/x/crosschain/keeper/gas_payment_test.go#L431` from `err = k.PayGasInERC20AndUpdateCctx(ctx, chainID, &cctx, math.NewUint(inputAmount), false)` to ` err = k.PayGasInERC20AndUpdateCctx(ctx, chainID, &cctx, math.NewUint(18446744073709551617), false)` and run it:

18446744073709551617 is 2^64+1, larger than 2^64-1.

Result shows below:

```json
./gas_payment_test.go:432:72: cannot use 18446744073709551617 (untyped int constant) as uint64 value in argument to math.NewUint (overflows)
```

https://github.com/code-423n4/2023-11-zetachain/blob/b237708ed5e86f12c4bddabddfd42f001e81941a/repos/node/x/crosschain/keeper/gas_payment_test.go#L431

## Tools Used

vscode

## Recommended Mitigation Steps

Use `2^256-1` to receive the `inputAmount` parameter.



## Assessed type

Under/Overflow
Don't check if the chain exists or not would cause node crash
Audit: 2023-11-zetachain-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact

`zeta_supply_checker#NewZetaSupplyChecker` doesn't check if the chain exists or not, and then call `chain.IsExternalChain()` directly, if the chain doesn't exist and return nil, it will cause node crash.

## Proof of Concept

Provide direct links to all referenced code in GitHub. Add screenshots, logs, or any other relevant proof that illustrates the concept.

## Tools Used

vscode

## Recommended Mitigation Steps

Return if the chain doesn't exist.






## Assessed type

Error
No way to recover tokens after user revert on onRevert
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/#L90
https://github.com/code-423n4/2023-11-zetachain/blob/b237708ed5e86f12c4bddabddfd42f001e81941a/repos/protocol-contracts/contracts/evm/ZetaConnector.non-eth.sol/#L101


# Vulnerability details

## Impact
User loses her funds after error on other chain with no way to recover them. 

## Proof of Concept

It is possible that there is some problem with the user’s `onZetaRevert` function. This could be a temporary mistake like when user doesn’t have funds at the current time of the callback.

Flow:
- TSS calls `onRevert`
- `ZetaReceiver(zetaTxSenderAddress).onZetaRevert` reverts with user error
- the token’s `remainingZetaValue` is stuck in `ZetaConnector.non-eth/eth/.sol`

## Tools Used
Manual review

## Recommended Mitigation Steps

There are a few options to recover, like trying to rerun `onRevert` by TSS, or allow user to try and rerun. None of which are ideal or implemented.

A clear solution wrap `onZetaRevert` in try/catch, and if it fails, then add the `remainingZetaValue` into a mapping from which user can withdraw later.

```solidity
+  try {
	    ZetaReceiver(zetaTxSenderAddress).onZetaRevert(
	        ZetaInterfaces.ZetaRevert(
	            zetaTxSenderAddress,
	            sourceChainId,
	            destinationAddress,
	            destinationChainId,
	            remainingZetaValue,
	            message
	        )
	    );
	}
+	catch {
+		toRefundToUser[destinationAddress] += remainingZetaValue;
+	}
```





## Assessed type

Token-Transfer