乐闻世界logo
搜索文章和话题

What are the differences between Library and Contract in Solidity? How to correctly use Library?

3月6日 21:50

Library is a special code reuse mechanism in Solidity that has essential differences from Contract. Correct use of Library can save Gas and improve code reusability.

1. Core Differences Between Library and Contract

FeatureLibraryContract
DeploymentCan be deployed independently or inlinedMust be deployed independently
State VariablesCannot have state variablesCan have state variables
InheritanceCannot be inheritedCan be inherited
ETH ReceptionCannot receive ETHCan receive ETH
Self-DestructionCannot self-destructCan self-destruct
Call MethodDELEGATECALL or inlineCALL
Use CasesUtility functions, code reuseBusiness logic, state management

2. Basic Usage of Library

Standalone Function Library

solidity
// File: MathLibrary.sol // Pure function library, all functions are pure library MathLibrary { // Calculate greatest common divisor function gcd(uint256 a, uint256 b) internal pure returns (uint256) { while (b != 0) { uint256 temp = b; b = a % b; a = temp; } return a; } // Calculate square root function sqrt(uint256 x) internal pure returns (uint256) { if (x == 0) return 0; uint256 z = (x + 1) / 2; uint256 y = x; while (z < y) { y = z; z = (x / z + z) / 2; } return y; } // Calculate power function pow(uint256 base, uint256 exponent) internal pure returns (uint256) { if (exponent == 0) return 1; if (exponent == 1) return base; uint256 result = 1; uint256 b = base; uint256 e = exponent; while (e > 0) { if (e & 1 == 1) { result *= b; } b *= b; e >>= 1; } return result; } } // Using the library contract Calculator { // Use using for syntax using MathLibrary for uint256; function calculate(uint256 a, uint256 b) external pure returns (uint256, uint256, uint256) { uint256 gcdResult = MathLibrary.gcd(a, b); uint256 sqrtResult = a.sqrt(); // Using using for syntax uint256 powResult = a.pow(b); // Using using for syntax return (gcdResult, sqrtResult, powResult); } }

Struct Library

solidity
// File: ArrayLibrary.sol library ArrayLibrary { // Find element index function find(uint256[] storage arr, uint256 value) internal view returns (int256) { for (uint i = 0; i < arr.length; i++) { if (arr[i] == value) { return int256(i); } } return -1; } // Remove element (maintain order) function removeAt(uint256[] storage arr, uint256 index) internal { require(index < arr.length, "Index out of bounds"); for (uint i = index; i < arr.length - 1; i++) { arr[i] = arr[i + 1]; } arr.pop(); } // Remove element (don't maintain order, more efficient) function quickRemove(uint256[] storage arr, uint256 index) internal { require(index < arr.length, "Index out of bounds"); if (index != arr.length - 1) { arr[index] = arr[arr.length - 1]; } arr.pop(); } // Insertion sort function sort(uint256[] storage arr) internal { for (uint i = 1; i < arr.length; i++) { uint256 key = arr[i]; int256 j = int256(i) - 1; while (j >= 0 && arr[uint256(j)] > key) { arr[uint256(j) + 1] = arr[uint256(j)]; j--; } arr[uint256(j) + 1] = key; } } // Check if contains function contains(uint256[] storage arr, uint256 value) internal view returns (bool) { for (uint i = 0; i < arr.length; i++) { if (arr[i] == value) { return true; } } return false; } } // Using struct library contract DataManager { using ArrayLibrary for uint256[]; uint256[] public data; function addData(uint256 value) external { data.push(value); } function removeData(uint256 index) external { data.quickRemove(index); // Call library function } function findData(uint256 value) external view returns (int256) { return data.find(value); // Call library function } function sortData() external { data.sort(); // Call library function } }

3. Advanced Usage of Library

Library Using Storage

solidity
// File: MappingLibrary.sol library MappingLibrary { // Add iteration capability to mapping struct IterableMapping { mapping(address => uint256) data; address[] keys; mapping(address => uint256) keyIndex; } function set(IterableMapping storage self, address key, uint256 value) internal { if (self.keyIndex[key] == 0) { // New key self.keys.push(key); self.keyIndex[key] = self.keys.length; } self.data[key] = value; } function get(IterableMapping storage self, address key) internal view returns (uint256) { return self.data[key]; } function remove(IterableMapping storage self, address key) internal { require(self.keyIndex[key] != 0, "Key not found"); uint256 index = self.keyIndex[key] - 1; address lastKey = self.keys[self.keys.length - 1]; // Replace element to be deleted with last element self.keys[index] = lastKey; self.keyIndex[lastKey] = index + 1; self.keys.pop(); delete self.keyIndex[key]; delete self.data[key]; } function contains(IterableMapping storage self, address key) internal view returns (bool) { return self.keyIndex[key] != 0; } function size(IterableMapping storage self) internal view returns (uint256) { return self.keys.length; } function getKeyAt(IterableMapping storage self, uint256 index) internal view returns (address) { require(index < self.keys.length, "Index out of bounds"); return self.keys[index]; } } // Using iterable mapping contract TokenHolder { using MappingLibrary for MappingLibrary.IterableMapping; MappingLibrary.IterableMapping private balances; function setBalance(address holder, uint256 amount) external { balances.set(holder, amount); } function getBalance(address holder) external view returns (uint256) { return balances.get(holder); } function removeHolder(address holder) external { balances.remove(holder); } function getAllHolders() external view returns (address[] memory) { address[] memory holders = new address[](balances.size()); for (uint i = 0; i < balances.size(); i++) { holders[i] = balances.getKeyAt(i); } return holders; } }

4. Common Standard Libraries

SafeMath (Solidity < 0.8.0)

solidity
// File: SafeMath.sol library SafeMath { function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; require(c >= a, "SafeMath: addition overflow"); return c; } function sub(uint256 a, uint256 b) internal pure returns (uint256) { require(b <= a, "SafeMath: subtraction underflow"); return a - b; } function mul(uint256 a, uint256 b) internal pure returns (uint256) { if (a == 0) return 0; uint256 c = a * b; require(c / a == b, "SafeMath: multiplication overflow"); return c; } function div(uint256 a, uint256 b) internal pure returns (uint256) { require(b > 0, "SafeMath: division by zero"); return a / b; } function mod(uint256 a, uint256 b) internal pure returns (uint256) { require(b != 0, "SafeMath: modulo by zero"); return a % b; } } // Using SafeMath contract SafeCalculator { using SafeMath for uint256; function safeAdd(uint256 a, uint256 b) external pure returns (uint256) { return a.add(b); // Use library function } }

Address Library

solidity
// OpenZeppelin's Address library library Address { function isContract(address account) internal view returns (bool) { return account.code.length > 0; } function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Insufficient balance"); (bool success, ) = recipient.call{value: amount}(""); require(success, "Transfer failed"); } function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCall(target, data, "Address: low-level call failed"); } function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { (bool success, bytes memory returndata) = target.call(data); if (!success) { if (returndata.length > 0) { assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } return returndata; } } // Using Address library contract ContractInteractor { using Address for address; function checkIsContract(address account) external view returns (bool) { return account.isContract(); } function safeTransfer(address payable recipient, uint256 amount) external { recipient.sendValue(amount); } }

5. Library Deployment Methods

Inline Library (Internal Functions)

solidity
// All functions are internal, code will be inlined into calling contract library InlineLibrary { function internalFunction(uint256 x) internal pure returns (uint256) { return x * 2; } } // No need to deploy library when using, code is inlined directly contract UsingInlineLibrary { using InlineLibrary for uint256; function double(uint256 x) external pure returns (uint256) { return x.internalFunction(); } }

External Library (External Functions)

solidity
// Contains external functions, needs to be deployed separately library ExternalLibrary { function externalFunction(uint256 x) external pure returns (uint256) { return x * 2; } } // Need to link deployed library address when using contract UsingExternalLibrary { function double(uint256 x) external pure returns (uint256) { return ExternalLibrary.externalFunction(x); } }

6. Best Practices for Library

Naming Conventions

solidity
// Library names use PascalCase, optionally ending with Library (recommended) library StringLibrary { // Function names use camelCase function toUpperCase(string memory str) internal pure returns (string memory) { // Implementation... } }

Function Visibility

solidity
library VisibilityExample { // internal: code inlined, no library deployment needed (recommended) function internalFunc() internal pure returns (uint256) { return 1; } // external: library needs to be deployed, called via DELEGATECALL function externalFunc() external pure returns (uint256) { return 2; } // public: can be called internally or externally function publicFunc() public pure returns (uint256) { return 3; } // private: can only be used inside library function privateFunc() private pure returns (uint256) { return 4; } }

Using Using For

solidity
library EnhancedMath { function square(uint256 x) internal pure returns (uint256) { return x * x; } function cube(uint256 x) internal pure returns (uint256) { return x * x * x; } } contract MathUser { // Method 1: Use using for for specific type using EnhancedMath for uint256; function calculate(uint256 x) external pure returns (uint256, uint256) { return (x.square(), x.cube()); } // Method 2: Global use (all uint256) using EnhancedMath for *; }

7. Practical Application Cases of Library

String Processing Library

solidity
library StringUtils { function length(string memory str) internal pure returns (uint256) { return bytes(str).length; } function concat(string memory a, string memory b) internal pure returns (string memory) { bytes memory ba = bytes(a); bytes memory bb = bytes(b); bytes memory result = new bytes(ba.length + bb.length); for (uint i = 0; i < ba.length; i++) { result[i] = ba[i]; } for (uint i = 0; i < bb.length; i++) { result[ba.length + i] = bb[i]; } return string(result); } function substring( string memory str, uint256 start, uint256 length ) internal pure returns (string memory) { bytes memory strBytes = bytes(str); require(start + length <= strBytes.length, "Out of bounds"); bytes memory result = new bytes(length); for (uint i = 0; i < length; i++) { result[i] = strBytes[start + i]; } return string(result); } function compare(string memory a, string memory b) internal pure returns (int256) { bytes memory ba = bytes(a); bytes memory bb = bytes(b); uint256 minLength = ba.length < bb.length ? ba.length : bb.length; for (uint i = 0; i < minLength; i++) { if (ba[i] < bb[i]) return -1; if (ba[i] > bb[i]) return 1; } if (ba.length < bb.length) return -1; if (ba.length > bb.length) return 1; return 0; } } // Using string library contract StringProcessor { using StringUtils for string; function processString(string memory input) external pure returns ( uint256 len, string memory sub, int256 cmp ) { len = input.length(); sub = input.substring(0, 5); cmp = input.compare("Hello"); } }

8. Gas Optimization with Library

solidity
// Before optimization: multiple code repetitions contract BeforeOptimization { function operation1(uint256 x) external pure returns (uint256) { // Complex calculation... return x * 2 + 1; } function operation2(uint256 x) external pure returns (uint256) { // Same complex calculation... return x * 2 + 1; } } // After optimization: use library to reuse code library OptimizedMath { function complexCalculation(uint256 x) internal pure returns (uint256) { return x * 2 + 1; } } contract AfterOptimization { using OptimizedMath for uint256; function operation1(uint256 x) external pure returns (uint256) { return x.complexCalculation(); } function operation2(uint256 x) external pure returns (uint256) { return x.complexCalculation(); } }

9. Summary

Library is a powerful code reuse tool in Solidity:

  1. Use Cases: Utility functions, data structure extensions, code reuse

  2. Deployment Methods: internal functions are inlined, external functions need deployment

  3. Best Practices:

    • Prioritize internal functions
    • Use using for syntax to improve readability
    • Follow naming conventions
    • Utilize standard libraries like OpenZeppelin
  4. Gas Optimization: Reduce code duplication, improve deployment efficiency

标签:Solidity