CurrencyTransferLib
Overview
CurrencyTransferLib is a utility library that provides safe and efficient functions for transferring ERC20 tokens and native ETH. It includes comprehensive error handling, gas optimization, and supports both standard token transfers and native currency operations.
Contract Specification
library CurrencyTransferLib
Library Type: Utility library for safe token transfers Usage: Used internally by staking contracts for all token operations
Constants
address public constant NATIVE_TOKEN = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
This constant represents native ETH transfers using a special address identifier.
Core Functions
Token Transfer Functions
transferCurrency
function transferCurrency(address currency, address to, uint256 amount) internal
Safely transfers tokens or native ETH to a specified address.
Parameters:
currency
: Token contract address (orNATIVE_TOKEN
for ETH)to
: Recipient addressamount
: Amount to transfer
Behavior:
- If
currency == NATIVE_TOKEN
: Transfers native ETH - Otherwise: Transfers ERC20 tokens using safe transfer
Error Handling:
- Reverts with
TransferFailed
if transfer fails - Includes recipient address and amount in error for debugging
transferCurrencyWithWrapper
function transferCurrencyWithWrapper(address currency, address to, uint256 amount, address wrapperToken) internal
Transfers tokens with wrapper token support for protocols that need WETH handling.
Parameters:
currency
: Token contract addressto
: Recipient addressamount
: Amount to transferwrapperToken
: Wrapper token address (e.g., WETH)
Behavior:
- If
currency == NATIVE_TOKEN
: Uses wrapper token for transfer - Otherwise: Standard ERC20 transfer
Transfer Verification Functions
safeTransferFrom
function safeTransferFrom(address token, address from, address to, uint256 amount) internal
Safely transfers tokens from one address to another with balance verification.
Parameters:
token
: ERC20 token contract addressfrom
: Source addressto
: Recipient addressamount
: Amount to transfer
Safety Features:
- Verifies balance before transfer
- Checks balance after transfer
- Handles tokens with non-standard return values
- Comprehensive error reporting
Process:
- Records recipient's balance before transfer
- Executes
transferFrom
call - Verifies balance increase matches expected amount
- Reverts if balance verification fails
safeTransfer
function safeTransfer(address token, address to, uint256 amount) internal
Safely transfers tokens from current contract to specified address.
Parameters:
token
: ERC20 token contract addressto
: Recipient addressamount
: Amount to transfer
Safety Features:
- Balance verification before and after transfer
- Handles non-standard ERC20 implementations
- Detailed error reporting
Native ETH Transfer Functions
safeTransferNativeToken
function safeTransferNativeToken(address to, uint256 value) internal
Safely transfers native ETH to specified address.
Parameters:
to
: Recipient addressvalue
: Amount of ETH to transfer in wei
Safety Features:
- Uses low-level call for gas efficiency
- Checks call success
- Reverts with descriptive error if transfer fails
safeTransferNativeTokenWithWrapper
function safeTransferNativeTokenWithWrapper(address to, uint256 value, address wrapperToken) internal
Transfers native ETH using wrapper token (WETH) for protocols requiring wrapped ETH.
Parameters:
to
: Recipient addressvalue
: Amount to transferwrapperToken
: Wrapper token address
Process:
- Uses IWETH interface to interact with wrapper
- Withdraws ETH from wrapper contract
- Transfers unwrapped ETH to recipient
Internal Helper Functions
Low-Level Transfer Operations
_callOptionalReturn
function _callOptionalReturn(address token, bytes memory data) private returns (bool)
Executes low-level call to token contract and handles return value variations.
Parameters:
token
: Token contract addressdata
: Encoded function call data
Returns:
- Boolean indicating success of the operation
Handling:
- Supports tokens that return bool values
- Supports tokens with no return value
- Validates return data length and value
_checkReturnValue
function _checkReturnValue(bool success, bytes memory returndata) private pure returns (bool)
Validates return data from token contract calls.
Parameters:
success
: Whether the low-level call succeededreturndata
: Return data from the call
Returns:
- Boolean indicating if the operation was successful
Validation Logic:
- If no return data: considers success if call succeeded
- If return data exists: validates it represents
true
Error Handling
Custom Errors
error TransferFailed(address to, uint256 amount);
error InsufficientBalance(address token, address account, uint256 required, uint256 available);
error TransferReturnValueInvalid(address token);
error NativeTokenTransferFailed(address to, uint256 amount);
Error Conditions
TransferFailed:
- Triggered when token transfer operation fails
- Includes recipient address and amount for debugging
InsufficientBalance:
- Triggered when account has insufficient token balance
- Provides detailed balance information
TransferReturnValueInvalid:
- Triggered when token returns invalid data
- Indicates non-compliant ERC20 implementation
NativeTokenTransferFailed:
- Triggered when ETH transfer fails
- Includes recipient and amount information
Token Compatibility
Standard ERC20 Tokens
Supports tokens that follow ERC20 standard:
- Return
true
on successful transfers - Revert on failed transfers
- Standard
transfer()
andtransferFrom()
functions
Non-Standard Tokens
Handles tokens with variations:
- Tokens that don't return values (e.g., USDT)
- Tokens that return
false
instead of reverting - Tokens with different return data formats
Balance Verification
Additional safety through balance checks:
- Compares balances before and after transfers
- Ensures expected amount was actually transferred
- Protects against tokens with transfer fees or rebasing mechanisms
Gas Optimization
Efficient ETH Transfers
- Uses low-level
call
for ETH transfers - Avoids unnecessary operations
- Minimizes gas overhead
Batch Operations
- Optimized for use in loops
- Efficient error handling
- Minimal storage operations
Call Pattern Optimization
- Single external call per operation
- Efficient return data handling
- Optimized assembly for balance checks
Integration Patterns
Staking Contract Usage
contract StakingContract {
using CurrencyTransferLib for address;
function stake(uint256 amount) external {
// Transfer tokens from user to contract
CurrencyTransferLib.safeTransferFrom(
stakingToken,
msg.sender,
address(this),
amount
);
}
function withdraw(uint256 amount) external {
// Transfer tokens from contract to user
CurrencyTransferLib.safeTransfer(
stakingToken,
msg.sender,
amount
);
}
}
Fee Collection
function collectFee(address token, uint256 feeAmount) internal {
if (token == CurrencyTransferLib.NATIVE_TOKEN) {
CurrencyTransferLib.safeTransferNativeToken(
feeCollector,
feeAmount
);
} else {
CurrencyTransferLib.safeTransfer(
token,
feeCollector,
feeAmount
);
}
}
Multi-Token Support
function transferMultipleTokens(
address[] calldata tokens,
address[] calldata recipients,
uint256[] calldata amounts
) external {
for (uint256 i = 0; i < tokens.length; i++) {
CurrencyTransferLib.transferCurrency(
tokens[i],
recipients[i],
amounts[i]
);
}
}
Security Features
Reentrancy Protection
- Uses
_callOptionalReturn
to minimize external call risks - Designed for use with ReentrancyGuard
- Safe for use in complex contract interactions
Balance Validation
- Pre and post-transfer balance checks
- Protection against fee-on-transfer tokens
- Verification of actual transferred amounts
Return Value Handling
- Comprehensive handling of different return patterns
- Protection against malicious token contracts
- Graceful handling of non-compliant tokens
Address Validation
- Zero address checks where appropriate
- Validation of contract addresses
- Protection against invalid transfer destinations
Wrapper Token Support
WETH Integration
The library provides special handling for WETH (Wrapped ETH):
interface IWETH {
function withdraw(uint256 amount) external;
function deposit() external payable;
}
Wrapper Usage Patterns
Deposit ETH to WETH:
- Contract receives ETH
- Wraps to WETH for protocol compatibility
- Transfers WETH to recipient
Withdraw WETH to ETH:
- Contract holds WETH
- Unwraps to native ETH
- Transfers ETH to recipient
Best Practices
Function Selection
Use safeTransfer
when:
- Transferring from contract to external address
- Known token balances
- Simple transfer operations
Use safeTransferFrom
when:
- Transferring from external address to contract
- User-initiated transfers
- Allowance-based transfers
Use transferCurrency
when:
- Supporting both ETH and ERC20 tokens
- Generic transfer operations
- Multi-token protocols
Error Handling
- Always handle transfer failures gracefully
- Provide meaningful error messages
- Include relevant addresses and amounts in errors
Gas Efficiency
- Batch transfers when possible
- Minimize external calls
- Use appropriate transfer function for context
Dependencies
External Interfaces
interface IERC20 {
function transfer(address to, uint256 amount) external returns (bool);
function transferFrom(address from, address to, uint256 amount) external returns (bool);
function balanceOf(address account) external view returns (uint256);
}
interface IWETH {
function withdraw(uint256 amount) external;
function deposit() external payable;
}
Assembly Usage
Limited assembly usage for:
- Return data length checks
- Low-level call result validation
- Gas-efficient balance comparisons
Testing Considerations
Token Variations
Test with tokens that:
- Return different values (true, false, no return)
- Have transfer fees
- Use different decimal places
- Implement non-standard behaviors
Edge Cases
- Zero amount transfers
- Transfers to zero address
- Insufficient balance scenarios
- Gas limit edge cases
Integration Testing
- Test with actual token contracts
- Verify behavior with different wallets
- Test wrapper token interactions
- Validate error reporting