Tadle

Tadle
DeFiFoundry
27,750 USDC
View results
Submission Details
Severity: low
Invalid

Incompatible native Token transfer on zkSync Era

Summary:

The tillIn function, designed to handle native token transfers by wrapping them and sending the wrapped tokens to a capital pool, fails when deployed on the zkSync Era platform due to zkSync's unique method of handling native tokens.

Description

The current implementation of the tillIn function attempts to wrap native tokens (e.g., Ether) into their ERC-20 equivalent (e.g., WETH) before transferring them to a capital pool. However, zkSync Era requires a different approach for native token transfers using the MsgValueSimulator system contract. This discrepancy leads to failures when the function is executed on zkSync Era.

/**
* @notice Till in, Transfer token from msg sender to capital pool
* @param _accountAddress Account address
* @param _tokenAddress Token address
* @param _amount Transfer amount
* @param _isPointToken The transfer token is pointToken
* @notice Capital pool should be deployed
* @dev Support ERC20 token and native token
*/
function tillIn(
address _accountAddress,
address _tokenAddress,
uint256 _amount,
bool _isPointToken
)
external
payable
onlyRelatedContracts(tadleFactory, _msgSender())
onlyInTokenWhiteList(_isPointToken, _tokenAddress)
{
/// @notice return if amount is 0
if (_amount == 0) {
return;
}
address capitalPoolAddr = tadleFactory.relatedContracts(
RelatedContractLibraries.CAPITAL_POOL
);
if (capitalPoolAddr == address(0x0)) {
revert Errors.ContractIsNotDeployed();
}
if (_tokenAddress == wrappedNativeToken) {
/**
* @dev token is native token
* @notice check msg value
* @dev if msg value is less than _amount, revert
* @dev wrap native token and transfer to capital pool
*/
if (msg.value < _amount) {
revert Errors.NotEnoughMsgValue(msg.value, _amount);
}
IWrappedNativeToken(wrappedNativeToken).deposit{value: _amount}();
_safe_transfer(wrappedNativeToken, capitalPoolAddr, _amount);
} else {
/// @notice token is ERC20 token
_transfer(
_tokenAddress,
_accountAddress,
capitalPoolAddr,
_amount,
capitalPoolAddr
);
}
emit TillIn(_accountAddress, _tokenAddress, _amount, _isPointToken);
}

Impact

When deployed on zkSync Era, the tillIn function will fail to process native token transfers correctly. This can cause transaction failures, leading to a poor user experience and potential disruption of the intended financial operations within the smart contract.

Root Cause

The root cause of this issue is the reliance on a wrapping mechanism for native tokens that is incompatible with zkSync Era's handling of native token transfers. The function does not account for zkSync's MsgValueSimulator, which is required to manage native tokens on that platform.

**reference: ** Zksync docs :: https://docs.zksync.io/zk-stack/components/compiler/specification/system-contracts

Tools Used

Manual Review

Recommendations

To resolve this issue, the tillIn function should be adapted to include support for zkSync Era's native token transfer mechanism via the MsgValueSimulator. This can be achieved by:

  1. Implementing a check to determine if the contract is deployed on zkSync Era.

  2. Utilizing the MsgValueSimulator for native token transfers when the contract is on zkSync Era.

  3. Retaining the original wrapping and transfer logic for deployments on other EVM-compatible chains.

Updates

Lead Judging Commences

0xnevi Lead Judge over 1 year ago
Submission Judgement Published
Invalidated
Reason: Other
Assigned finding tags:

[invalid] finding-zkSync-low-level-call

I would require a more explicit example on zkSync mainnet to prove that such a low level call is indeed an issue, i.e. a example live contract on zkSync that shows the usual low level `call()` cannot be utilized.

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.

Give us feedback!