From e3dd33ceee8f803510cbd0debb8ed55fef4007e8 Mon Sep 17 00:00:00 2001 From: Stanislav Bezkorovainyi Date: Mon, 10 Feb 2025 11:07:54 +0100 Subject: [PATCH] Fix token fetching (#1245) Co-authored-by: nikitastupin-matterlabs <178824211+nikitastupin-matterlabs@users.noreply.github.com> --- .../contracts/upgrades/L1GatewayBase.sol | 54 +++++++++++++++---- 1 file changed, 43 insertions(+), 11 deletions(-) diff --git a/l1-contracts/contracts/upgrades/L1GatewayBase.sol b/l1-contracts/contracts/upgrades/L1GatewayBase.sol index 16a56253a..fa1126143 100644 --- a/l1-contracts/contracts/upgrades/L1GatewayBase.sol +++ b/l1-contracts/contracts/upgrades/L1GatewayBase.sol @@ -12,10 +12,14 @@ import {ZKChainStorage} from "../state-transition/chain-deps/ZKChainStorage.sol" import {L2WrappedBaseTokenStore} from "../bridge/L2WrappedBaseTokenStore.sol"; import {IERC20Metadata} from "@openzeppelin/contracts-v4/token/ERC20/extensions/IERC20Metadata.sol"; +import {UnsafeBytes} from "../common/libraries/UnsafeBytes.sol"; + /// @title L1GatewayBase /// @author Matter Labs /// @custom:security-contact security@matterlabs.dev abstract contract L1GatewayBase { + using UnsafeBytes for bytes; + /// @notice The function to retrieve the chain-specific upgrade data. /// @param s The pointer to the storage of the chain. /// @param _wrappedBaseTokenStore The address of the `L2WrappedBaseTokenStore` contract. @@ -43,15 +47,23 @@ abstract contract L1GatewayBase { baseTokenName = string("Ether"); baseTokenSymbol = string("ETH"); } else { - try this.getTokenName(_baseTokenAddress) returns (string memory name) { - baseTokenName = name; - } catch { + (string memory stringResult, bool success) = _safeCallTokenMetadata( + _baseTokenAddress, + abi.encodeCall(IERC20Metadata.name, ()) + ); + if (success) { + baseTokenName = stringResult; + } else { baseTokenName = string("Base Token"); } - try this.getTokenSymbol(_baseTokenAddress) returns (string memory symbol) { - baseTokenSymbol = symbol; - } catch { + (stringResult, success) = _safeCallTokenMetadata( + _baseTokenAddress, + abi.encodeCall(IERC20Metadata.symbol, ()) + ); + if (success) { + baseTokenSymbol = stringResult; + } else { // "BT" is an acronym for "Base Token" baseTokenSymbol = string("BT"); } @@ -69,11 +81,31 @@ abstract contract L1GatewayBase { return abi.encode(additionalForceDeploymentsData); } - function getTokenName(address _token) external view returns (string memory) { - return IERC20Metadata(_token).name(); - } + /// @notice Calls a token's metadata method. + /// @dev For the sake of simplicity, we expect that either of the + /// following is true: + /// 1. The token does not support metadata methods + /// 2. The token supports it and returns a `bytes32` string there. + /// 3. The token supports it and returns a correct `string` as a returndata. + /// + /// For all other cases, this function will panic and so such chains would not be + /// deployable. + function _safeCallTokenMetadata(address _token, bytes memory data) internal view returns (string memory, bool) { + // We are not afraid if token returns large calldata, since it affects + // only the deployment of the chain that uses such a malicious token. + (bool callSuccess, bytes memory returnData) = _token.staticcall(data); + + // The failed call most likely means that this method is not supported. + if (!callSuccess) { + return ("", false); + } + + // This case covers non-standard tokens, such as Maker (MKR), that return `bytes32` instead of `string` + if (returnData.length == 32) { + return ("", false); + } - function getTokenSymbol(address _token) external view returns (string memory) { - return IERC20Metadata(_token).symbol(); + // Note, that the following line will panic in case the token has more non-standard behavior. + return (abi.decode(returnData, (string)), true); } }