Skip to content

Commit

Permalink
fix(protocol): fix metadata retrieval in vaults (#17003)
Browse files Browse the repository at this point in the history
Co-authored-by: dantaik <[email protected]>
  • Loading branch information
dantaik and dantaik authored May 6, 2024
1 parent d048a28 commit 658775a
Show file tree
Hide file tree
Showing 6 changed files with 97 additions and 76 deletions.
46 changes: 23 additions & 23 deletions packages/protocol/contract_layout.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,29 +18,29 @@
| __gap | uint256[50] | 301 | 0 | 1600 | contracts/L1/TaikoL1.sol:TaikoL1 |

## TaikoL2
| Name | Type | Slot | Offset | Bytes | Contract |
|-------------------------|-----------------------------|------|--------|-------|----------------------------------|
| _initialized | uint8 | 0 | 0 | 1 | contracts/L2/TaikoL2.sol:TaikoL2 |
| _initializing | bool | 0 | 1 | 1 | contracts/L2/TaikoL2.sol:TaikoL2 |
| __gap | uint256[50] | 1 | 0 | 1600 | contracts/L2/TaikoL2.sol:TaikoL2 |
| _owner | address | 51 | 0 | 20 | contracts/L2/TaikoL2.sol:TaikoL2 |
| __gap | uint256[49] | 52 | 0 | 1568 | contracts/L2/TaikoL2.sol:TaikoL2 |
| _pendingOwner | address | 101 | 0 | 20 | contracts/L2/TaikoL2.sol:TaikoL2 |
| __gap | uint256[49] | 102 | 0 | 1568 | contracts/L2/TaikoL2.sol:TaikoL2 |
| addressManager | address | 151 | 0 | 20 | contracts/L2/TaikoL2.sol:TaikoL2 |
| __gap | uint256[49] | 152 | 0 | 1568 | contracts/L2/TaikoL2.sol:TaikoL2 |
| __reentry | uint8 | 201 | 0 | 1 | contracts/L2/TaikoL2.sol:TaikoL2 |
| __paused | uint8 | 201 | 1 | 1 | contracts/L2/TaikoL2.sol:TaikoL2 |
| lastUnpausedAt | uint64 | 201 | 2 | 8 | contracts/L2/TaikoL2.sol:TaikoL2 |
| __gap | uint256[49] | 202 | 0 | 1568 | contracts/L2/TaikoL2.sol:TaikoL2 |
| l2Hashes | mapping(uint256 => bytes32) | 251 | 0 | 32 | contracts/L2/TaikoL2.sol:TaikoL2 |
| publicInputHash | bytes32 | 252 | 0 | 32 | contracts/L2/TaikoL2.sol:TaikoL2 |
| gasExcess | uint64 | 253 | 0 | 8 | contracts/L2/TaikoL2.sol:TaikoL2 |
| lastSyncedBlock | uint64 | 253 | 8 | 8 | contracts/L2/TaikoL2.sol:TaikoL2 |
| parentTimestamp | uint64 | 253 | 16 | 8 | contracts/L2/TaikoL2.sol:TaikoL2 |
| __currentBlockTimestamp | uint64 | 253 | 24 | 8 | contracts/L2/TaikoL2.sol:TaikoL2 |
| l1ChainId | uint64 | 254 | 0 | 8 | contracts/L2/TaikoL2.sol:TaikoL2 |
| __gap | uint256[46] | 255 | 0 | 1472 | contracts/L2/TaikoL2.sol:TaikoL2 |
| Name | Type | Slot | Offset | Bytes | Contract |
|-----------------|-----------------------------|------|--------|-------|----------------------------------|
| _initialized | uint8 | 0 | 0 | 1 | contracts/L2/TaikoL2.sol:TaikoL2 |
| _initializing | bool | 0 | 1 | 1 | contracts/L2/TaikoL2.sol:TaikoL2 |
| __gap | uint256[50] | 1 | 0 | 1600 | contracts/L2/TaikoL2.sol:TaikoL2 |
| _owner | address | 51 | 0 | 20 | contracts/L2/TaikoL2.sol:TaikoL2 |
| __gap | uint256[49] | 52 | 0 | 1568 | contracts/L2/TaikoL2.sol:TaikoL2 |
| _pendingOwner | address | 101 | 0 | 20 | contracts/L2/TaikoL2.sol:TaikoL2 |
| __gap | uint256[49] | 102 | 0 | 1568 | contracts/L2/TaikoL2.sol:TaikoL2 |
| addressManager | address | 151 | 0 | 20 | contracts/L2/TaikoL2.sol:TaikoL2 |
| __gap | uint256[49] | 152 | 0 | 1568 | contracts/L2/TaikoL2.sol:TaikoL2 |
| __reentry | uint8 | 201 | 0 | 1 | contracts/L2/TaikoL2.sol:TaikoL2 |
| __paused | uint8 | 201 | 1 | 1 | contracts/L2/TaikoL2.sol:TaikoL2 |
| lastUnpausedAt | uint64 | 201 | 2 | 8 | contracts/L2/TaikoL2.sol:TaikoL2 |
| __gap | uint256[49] | 202 | 0 | 1568 | contracts/L2/TaikoL2.sol:TaikoL2 |
| l2Hashes | mapping(uint256 => bytes32) | 251 | 0 | 32 | contracts/L2/TaikoL2.sol:TaikoL2 |
| publicInputHash | bytes32 | 252 | 0 | 32 | contracts/L2/TaikoL2.sol:TaikoL2 |
| gasExcess | uint64 | 253 | 0 | 8 | contracts/L2/TaikoL2.sol:TaikoL2 |
| lastSyncedBlock | uint64 | 253 | 8 | 8 | contracts/L2/TaikoL2.sol:TaikoL2 |
| __deprecated1 | uint64 | 253 | 16 | 8 | contracts/L2/TaikoL2.sol:TaikoL2 |
| __deprecated2 | uint64 | 253 | 24 | 8 | contracts/L2/TaikoL2.sol:TaikoL2 |
| l1ChainId | uint64 | 254 | 0 | 8 | contracts/L2/TaikoL2.sol:TaikoL2 |
| __gap | uint256[46] | 255 | 0 | 1472 | contracts/L2/TaikoL2.sol:TaikoL2 |

## SignalService
| Name | Type | Slot | Offset | Bytes | Contract |
Expand Down
32 changes: 32 additions & 0 deletions packages/protocol/contracts/libs/LibBytes.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

library LibBytes {
// Taken from:
// https://github.com/0xPolygonHermez/zkevm-contracts/blob/main/contracts/PolygonZkEVMBridge.sol#L835-L860
/// @notice Function to convert returned data to string
/// returns 'NOT_VALID_ENCODING' as fallback value.
function toString(bytes memory _data) internal pure returns (string memory) {
if (_data.length >= 64) {
return abi.decode(_data, (string));
} else if (_data.length == 32) {
// Since the strings on bytes32 are encoded left-right, check the first zero in the data
uint256 nonZeroBytes;
while (nonZeroBytes < 32 && _data[nonZeroBytes] != 0) {
++nonZeroBytes;
}

// If the first one is 0, we do not handle the encoding
if (nonZeroBytes == 0) return "";

// Create a byte array with nonZeroBytes length
bytes memory bytesArray = new bytes(nonZeroBytes);
for (uint256 i; i < nonZeroBytes; ++i) {
bytesArray[i] = _data[i];
}
return string(bytesArray);
} else {
return "";
}
}
}
25 changes: 25 additions & 0 deletions packages/protocol/contracts/tokenvault/BaseVault.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,17 @@ import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
import "../bridge/IBridge.sol";
import "../common/EssentialContract.sol";
import "../common/LibStrings.sol";
import "../libs/LibBytes.sol";

/// @title INameSymbol
/// @notice Interface for contracts that provide name() and symbol()
/// functions. These functions may not be part of the official interface but are
/// used by some contracts.
/// @custom:security-contact [email protected]
interface INameSymbol {
function name() external view returns (string memory);
function symbol() external view returns (string memory);
}

/// @title BaseVault
/// @notice This abstract contract provides a base implementation for vaults.
Expand All @@ -16,6 +27,8 @@ abstract contract BaseVault is
IMessageInvocable,
IERC165Upgradeable
{
using LibBytes for bytes;

uint256[50] private __gap;

error VAULT_INVALID_TO_ADDR();
Expand Down Expand Up @@ -58,4 +71,16 @@ abstract contract BaseVault is
function checkToAddress(address _to) internal view {
if (_to == address(0) || _to == address(this)) revert VAULT_INVALID_TO_ADDR();
}

function safeSymbol(address _token) internal view returns (string memory symbol_) {
(bool success, bytes memory data) =
address(_token).staticcall(abi.encodeCall(INameSymbol.symbol, ()));
return success ? data.toString() : "";
}

function safeName(address _token) internal view returns (string memory) {
(bool success, bytes memory data) =
address(_token).staticcall(abi.encodeCall(INameSymbol.name, ()));
return success ? data.toString() : "";
}
}
24 changes: 2 additions & 22 deletions packages/protocol/contracts/tokenvault/ERC1155Vault.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,6 @@ import "../common/LibStrings.sol";
import "./BaseNFTVault.sol";
import "./BridgedERC1155.sol";

/// @title IERC1155NameAndSymbol
/// @notice Interface for ERC1155 contracts that provide name() and symbol()
/// functions. These functions may not be part of the official interface but are
/// used by some contracts.
/// @custom:security-contact [email protected]
interface IERC1155NameAndSymbol {
/// @notice Returns the name of the token.
function name() external view returns (string memory);

/// @notice Returns the symbol of the token.
function symbol() external view returns (string memory);
}

/// @title ERC1155Vault
/// @dev Labeled in AddressResolver as "erc1155_vault"
/// @notice This vault holds all ERC1155 tokens that users have deposited.
Expand Down Expand Up @@ -257,16 +244,9 @@ contract ERC1155Vault is BaseNFTVault, ERC1155ReceiverUpgradeable {
ctoken_ = CanonicalNFT({
chainId: uint64(block.chainid),
addr: _op.token,
symbol: "",
name: ""
symbol: safeSymbol(_op.token),
name: safeName(_op.token)
});
IERC1155NameAndSymbol t = IERC1155NameAndSymbol(_op.token);
try t.name() returns (string memory _name) {
ctoken_.name = _name;
} catch { }
try t.symbol() returns (string memory _symbol) {
ctoken_.symbol = _symbol;
} catch { }

IERC1155(_op.token).safeBatchTransferFrom({
from: msg.sender,
Expand Down
27 changes: 10 additions & 17 deletions packages/protocol/contracts/tokenvault/ERC20Vault.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "../bridge/IQuotaManager.sol";
import "../libs/LibAddress.sol";
import "../common/LibStrings.sol";
import "../libs/LibAddress.sol";
import "./BridgedERC20.sol";
import "./BaseVault.sol";

Expand Down Expand Up @@ -367,24 +367,11 @@ contract ERC20Vault is BaseVault {
ctoken_ = CanonicalERC20({
chainId: uint64(block.chainid),
addr: _op.token,
decimals: 0,
symbol: "",
name: ""
decimals: _safeDecimals(_op.token),
symbol: safeSymbol(_op.token),
name: safeName(_op.token)
});

// Try fill in the boilerplate values, but use try-catch because functions below are
// ERC20-optional only.
IERC20Metadata meta = IERC20Metadata(_op.token);
try meta.decimals() returns (uint8 _decimals) {
ctoken_.decimals = _decimals;
} catch { }
try meta.name() returns (string memory _name) {
ctoken_.name = _name;
} catch { }
try meta.symbol() returns (string memory _symbol) {
ctoken_.symbol = _symbol;
} catch { }

// Query the balance then query it again to get the actual amount of
// token transferred into this address, this is more accurate than
// simply using `amount` -- some contract may deduct a fee from the
Expand Down Expand Up @@ -453,4 +440,10 @@ contract ERC20Vault is BaseVault {
IQuotaManager(quotaManager).consumeQuota(_token, _amount);
}
}

function _safeDecimals(address _token) private view returns (uint8) {
(bool success, bytes memory data) =
address(_token).staticcall(abi.encodeCall(IERC20Metadata.decimals, ()));
return success && data.length == 32 ? abi.decode(data, (uint8)) : 18;
}
}
19 changes: 5 additions & 14 deletions packages/protocol/contracts/tokenvault/ERC721Vault.sol
Original file line number Diff line number Diff line change
Expand Up @@ -200,26 +200,17 @@ contract ERC721Vault is BaseNFTVault, IERC721Receiver {
BridgedERC721(_op.token).burn(msg.sender, _op.tokenIds[i]);
}
} else {
ERC721Upgradeable t = ERC721Upgradeable(_op.token);

ctoken_ = CanonicalNFT({
chainId: uint64(block.chainid),
addr: _op.token,
symbol: "",
name: ""
symbol: safeSymbol(_op.token),
name: safeName(_op.token)
});

// Try fill in the boilerplate values, but use try-catch because functions below are
// ERC20-optional only.
try t.name() returns (string memory _name) {
ctoken_.name = _name;
} catch { }
try t.symbol() returns (string memory _symbol) {
ctoken_.symbol = _symbol;
} catch { }

for (uint256 i; i < _op.tokenIds.length; ++i) {
t.safeTransferFrom(msg.sender, address(this), _op.tokenIds[i]);
ERC721Upgradeable(_op.token).safeTransferFrom(
msg.sender, address(this), _op.tokenIds[i]
);
}
}
}
Expand Down

0 comments on commit 658775a

Please sign in to comment.