Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions src/libs/TransferHelper.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.6.0 <0.9.0;

import { IERC20 } from "../interfaces/IERC20.sol";

/// @notice Safe ERC20 transfer library that safely handles missing return values.
/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v3-periphery/blob/main/contracts/libraries/TransferHelper.sol)
/// @dev Only change from Uniswap is the addition of the "safeTransferExact*" function variants which check that there was no fee on transfer.
library TransferHelper {
/// @notice Transfers tokens from the targeted address to the given destination
/// @dev Errors with 'STF' if transfer fails
/// @param token The contract address of the token to be transferred
/// @param from The originating address from which the tokens will be transferred
/// @param to The destination address of the transfer
/// @param value The amount to be transferred
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
(bool success, bytes memory data) = address(token).call(
abi.encodeWithSelector(IERC20.transferFrom.selector, from, to, value)
);
require(success && (data.length == 0 || abi.decode(data, (bool))), "STF");
}

/// @notice Transfer tokens from the targeted address to the given destination with a balance check
/// @dev Errors with 'STFE' if transfer fails or there is a fee on transfer
/// @param token The contract address of the token to be transferred
/// @param from The originating address from which the tokens will be transferred
/// @param to The destination address of the transfer
/// @param value The amount to be transferred
function safeTransferExactFrom(IERC20 token, address from, address to, uint256 value) internal {
uint256 balanceBefore = token.balanceOf(to);
safeTransferFrom(token, from, to, value);
require(token.balanceOf(to) - balanceBefore >= value, "STFE");
}

/// @notice Transfers tokens from msg.sender to a recipient
/// @dev Errors with ST if transfer fails
/// @param token The contract address of the token which will be transferred
/// @param to The recipient of the transfer
/// @param value The value of the transfer
function safeTransfer(IERC20 token, address to, uint256 value) internal {
(bool success, bytes memory data) = address(token).call(
abi.encodeWithSelector(IERC20.transfer.selector, to, value)
);
require(success && (data.length == 0 || abi.decode(data, (bool))), "ST");
}

/// @notice Transfer tokens from msg.sender to a recipient with a balance check
/// @dev Errors with 'STE' if transfer fails or there is a fee on transfer
/// @param token The contract address of the token which will be transferred
/// @param to The recipient of the transfer
/// @param value The value of the transfer
function safeTransferExact(IERC20 token, address to, uint256 value) internal {
uint256 balanceBefore = token.balanceOf(to);
safeTransfer(token, to, value);
require(token.balanceOf(to) - balanceBefore >= value, "STE");
}

/// @notice Approves the stipulated contract to spend the given allowance in the given token
/// @dev Errors with 'SA' if transfer fails
/// @param token The contract address of the token to be approved
/// @param to The target of the approval
/// @param value The amount of the given token the target will be allowed to spend
function safeApprove(IERC20 token, address to, uint256 value) internal {
(bool success, bytes memory data) = address(token).call(
abi.encodeWithSelector(IERC20.approve.selector, to, value)
);
require(success && (data.length == 0 || abi.decode(data, (bool))), "SA");
}
}
57 changes: 57 additions & 0 deletions src/libs/TypeConverter.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.8.20 <0.9.0;

/// @title TypeConverter
/// @author M0 Labs
/// @notice Utilities for converting between different data types.
library TypeConverter {
/// @notice Thrown when a uint256 value exceeds the max uint64 value.
error Uint64Overflow();
/// @notice Thrown when a uint256 value exceeds the max uint128 value.
error Uint128Overflow();

/// @notice Thrown when a bytes32 value doesn't represent a valid Ethereum address.
error InvalidAddress(bytes32 value);

/// @notice Converts a uint256 to uint64, reverting if the value overflows.
/// @param value The uint256 value to convert.
/// @return The uint64 representation of the value.
function toUint64(uint256 value) internal pure returns (uint64) {
if (value > type(uint64).max) revert Uint64Overflow();
return uint64(value);
}

/// @notice Converts a uint256 to uint128, reverting if the value overflows.
/// @param value The uint256 value to convert.
/// @return The uint128 representation of the value.
function toUint128(uint256 value) internal pure returns (uint128) {
if (value > type(uint128).max) revert Uint128Overflow();
return uint128(value);
}

/// @notice Convert an Ethereum address to bytes32.
/// @dev Pads the 20-byte address to 32 bytes by converting to uint160, then uint256, then bytes32.
/// @param addressValue The address to convert.
/// @return The bytes32 representation of the address.
function toBytes32(address addressValue) internal pure returns (bytes32) {
return bytes32(uint256(uint160(addressValue)));
}

/// @notice Convert bytes32 to an Ethereum address.
/// @dev Truncates the 32-byte value to 20 bytes by converting to uint256, then uint160, then address.
/// @param bytes32Value The bytes32 value to convert.
/// @return The address representation of the bytes32 value.
function toAddress(bytes32 bytes32Value) internal pure returns (address) {
if (!isValidAddress(bytes32Value)) revert InvalidAddress(bytes32Value);
return address(uint160(uint256(bytes32Value)));
}

/// @notice Check if a bytes32 value represents a valid Ethereum address.
/// @dev An Ethereum address must have the top 12 bytes as zero.
/// @param bytes32Value The bytes32 value to check.
/// @return True if the bytes32 value can be safely converted to an Ethereum address.
function isValidAddress(bytes32 bytes32Value) internal pure returns (bool) {
// The top 12 bytes must be zero for a valid Ethereum address
return uint256(bytes32Value) >> 160 == 0;
}
}
Loading