144 lines
5.3 KiB
Solidity
144 lines
5.3 KiB
Solidity
// SPDX-License-Identifier: MIT
|
|
pragma solidity >=0.8.4;
|
|
|
|
import { ERC20 } from "../tokens/ERC20.sol";
|
|
|
|
/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
|
|
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SafeTransferLib.sol)
|
|
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
|
|
/// @dev Caution! This library won't check that a token has code, responsibility is delegated to the caller.
|
|
library SafeTransferLib {
|
|
/*//////////////////////////////////////////////////////////////
|
|
CUSTOM ERRORS
|
|
//////////////////////////////////////////////////////////////*/
|
|
|
|
/// @dev The ETH transfer has failed.
|
|
error ETHTransferFailed();
|
|
/// @dev The ERC20 `transferFrom` has failed.
|
|
error TransferFromFailed();
|
|
/// @dev The ERC20 `transfer` has failed.
|
|
error TransferFailed();
|
|
|
|
/*//////////////////////////////////////////////////////////////
|
|
ETH OPERATIONS
|
|
//////////////////////////////////////////////////////////////*/
|
|
|
|
/// @dev Sends `amount` (in wei) ETH to `to`.
|
|
/// Reverts upon failure.
|
|
function safeTransferETH(
|
|
address to,
|
|
uint256 amount
|
|
) internal {
|
|
/// @solidity memory-safe-assembly
|
|
assembly {
|
|
// Transfer the ETH and check if it succeeded or not.
|
|
if iszero(call(gas(), to, amount, 0, 0, 0, 0)) {
|
|
// Store the function selector of `ETHTransferFailed()`.
|
|
mstore(0x00, 0xb12d13eb)
|
|
// Revert with (offset, size).
|
|
revert(0x1c, 0x04)
|
|
}
|
|
}
|
|
}
|
|
|
|
/*//////////////////////////////////////////////////////////////
|
|
ERC20 OPERATIONS
|
|
//////////////////////////////////////////////////////////////*/
|
|
|
|
/// @dev Sends `amount` of ERC20 `token` from `from` to `to`.
|
|
/// Reverts upon failure.
|
|
///
|
|
/// The `from` account must have at least `amount` approved for
|
|
/// the current contract to manage.
|
|
function safeTransferFrom(
|
|
ERC20 token,
|
|
address from,
|
|
address to,
|
|
uint256 amount
|
|
) internal {
|
|
/// @solidity memory-safe-assembly
|
|
assembly {
|
|
let m := mload(0x40) // Cache the free memory pointer.
|
|
|
|
mstore(0x60, amount) // Store the `amount` argument.
|
|
mstore(0x40, to) // Store the `to` argument.
|
|
mstore(0x2c, shl(96, from)) // Store the `from` argument.
|
|
// Store the function selector of `transferFrom(address,address,uint256)`.
|
|
mstore(0x0c, 0x23b872dd000000000000000000000000)
|
|
|
|
if iszero(
|
|
and(
|
|
// The arguments of `and` are evaluated from right to left.
|
|
// Set success to whether the call reverted, if not we check it either
|
|
// returned exactly 1 (can't just be non-zero data), or had no return data.
|
|
or(
|
|
eq(mload(0x00), 1),
|
|
iszero(returndatasize())
|
|
),
|
|
call(
|
|
gas(),
|
|
token,
|
|
0,
|
|
0x1c,
|
|
0x64,
|
|
0x00,
|
|
0x20
|
|
)
|
|
)
|
|
) {
|
|
// Store the function selector of `TransferFromFailed()`.
|
|
mstore(0x00, 0x7939f424)
|
|
// Revert with (offset, size).
|
|
revert(0x1c, 0x04)
|
|
}
|
|
|
|
mstore(0x60, 0) // Restore the zero slot to zero.
|
|
mstore(0x40, m) // Restore the free memory pointer.
|
|
}
|
|
}
|
|
|
|
/// @dev Sends `amount` of ERC20 `token` from the current contract to `to`.
|
|
/// Reverts upon failure.
|
|
function safeTransfer(
|
|
ERC20 token,
|
|
address to,
|
|
uint256 amount
|
|
) internal {
|
|
/// @solidity memory-safe-assembly
|
|
assembly {
|
|
mstore(0x14, to) // Store the `to` argument.
|
|
mstore(0x34, amount) // Store the `amount` argument.
|
|
// Store the function selector of `transfer(address,uint256)`.
|
|
mstore(0x00, 0xa9059cbb000000000000000000000000)
|
|
|
|
if iszero(
|
|
and(
|
|
// The arguments of `and` are evaluated from right to left.
|
|
// Set success to whether the call reverted, if not we check it either
|
|
// returned exactly 1 (can't just be non-zero data), or had no return data.
|
|
or(
|
|
eq(mload(0x00), 1),
|
|
iszero(returndatasize())
|
|
),
|
|
call(
|
|
gas(),
|
|
token,
|
|
0,
|
|
0x10,
|
|
0x44,
|
|
0x00,
|
|
0x20
|
|
)
|
|
)
|
|
) {
|
|
// Store the function selector of `TransferFailed()`.
|
|
mstore(0x00, 0x90b8ec18)
|
|
// Revert with (offset, size).
|
|
revert(0x1c, 0x04)
|
|
}
|
|
// Restore the part of the free memory pointer that was overwritten.
|
|
mstore(0x34, 0)
|
|
}
|
|
}
|
|
}
|