Skip to content

Commit 2a49fd2

Browse files
committed
Add Libraries
1 parent 6087ee4 commit 2a49fd2

File tree

1 file changed

+183
-0
lines changed

1 file changed

+183
-0
lines changed

README.md

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,13 @@ _This cheatsheet is based on version 0.8.29_
101101
- [Abstract Contracts](#abstract-contracts)
102102
- [Diamond Inheritance and the “Linearization of Base Contracts”](#diamond-inheritance-and-the-linearization-of-base-contracts)
103103
- [Best Practices](#best-practices-1)
104+
- [Libraries](#libraries)
105+
- [Key Characteristics of Libraries](#key-characteristics-of-libraries)
106+
- [Library Function Types](#library-function-types)
107+
- [Example: Internal Library](#example-internal-library)
108+
- [Example: External Library](#example-external-library)
109+
- [Library for Struct Extensions](#library-for-struct-extensions)
110+
- [Best Practices](#best-practices-2)
104111
- [References](#references)
105112

106113
# Getting Started
@@ -1566,6 +1573,182 @@ contract D is B, C {
15661573
- Overlapping storage layouts from multiple parents can cause confusion or even storage collisions.
15671574
- Ensure each parent contract uses distinct state variable slots (this usually happens naturally if you’re just inheriting standard contracts without complicated overrides).
15681575

1576+
# Libraries
1577+
1578+
- In Solidity, **libraries** are special types of contracts intended to provide reusable code.
1579+
- They can be thought of as utility or helper modules that other contracts can call without needing to implement the same logic repeatedly.
1580+
- Libraries help keep code DRY (Don’t Repeat Yourself), save gas, and improve readability.
1581+
1582+
## Key Characteristics of Libraries
1583+
1584+
- **No Ether**
1585+
1586+
- Libraries **cannot** hold Ether (i.e., they cannot have a balance).
1587+
- Any attempt to send Ether to a library will fail.
1588+
1589+
- **No State Variables**
1590+
1591+
- Libraries cannot store or modify their own state; they are stateless.
1592+
1593+
- **No Inheritance**
1594+
1595+
- Libraries cannot be inherited or extend other contracts (nor can you inherit from a library).
1596+
1597+
- **Linked at Compile Time or Deployment Time**
1598+
- **Internal library functions** are typically inlined or statically called.
1599+
- **External library functions** can be deployed and then “linked” to your contract.
1600+
1601+
## Library Function Types
1602+
1603+
Solidity libraries support **two** ways of using their functions:
1604+
1605+
- **Internal Functions**
1606+
1607+
- If a library function is declared `internal`, it is inlined into the calling contract’s bytecode at compile time (similar to macros).
1608+
- This reduces overhead since no external call is made.
1609+
1610+
- **External Functions**
1611+
1612+
- If a library function is declared `public` or `external`, the calling contract will **delegatecall** into the library at runtime.
1613+
- This can save bytecode size in the calling contract but introduces a small overhead for each external call.
1614+
- You must **deploy** the library contract first and link it before deploying the final contract.
1615+
1616+
### Example: Internal Library
1617+
1618+
Most libraries are used as **internal** because it’s more gas-efficient (no external call) and simpler to manage.
1619+
1620+
```solidity
1621+
// MathLib.sol
1622+
pragma solidity ^0.8.29;
1623+
1624+
library MathLib {
1625+
function add(uint256 a, uint256 b) internal pure returns (uint256) {
1626+
return a + b;
1627+
}
1628+
1629+
function multiply(uint256 a, uint256 b) internal pure returns (uint256) {
1630+
return a * b;
1631+
}
1632+
}
1633+
1634+
// TestMath.sol
1635+
pragma solidity ^0.8.21;
1636+
1637+
import "./MathLib.sol";
1638+
1639+
contract TestMath {
1640+
function testAdd(uint256 x, uint256 y) public pure returns (uint256) {
1641+
return MathLib.add(x, y);
1642+
}
1643+
1644+
function testMultiply(uint256 x, uint256 y) public pure returns (uint256) {
1645+
return MathLib.multiply(x, y);
1646+
}
1647+
}
1648+
```
1649+
1650+
- `MathLib.add` and `MathLib.multiply` are called from `TestMath` without an external call because they’re `internal` functions in a library.
1651+
- The compiler will inline or statically link these calls, increasing `TestMath`’s bytecode size, but saving on runtime gas costs.
1652+
1653+
### Example: External Library
1654+
1655+
You can define a library with public or external functions. In that case, your contract calls the library via delegatecall at runtime.
1656+
1657+
```solidity
1658+
// ExternalLib.sol
1659+
pragma solidity ^0.8.29;
1660+
1661+
library ExternalLib {
1662+
function externalAdd(uint256 a, uint256 b) external pure returns (uint256) {
1663+
return a + b;
1664+
}
1665+
}
1666+
```
1667+
1668+
To use this library in another contract:
1669+
1670+
```solidity
1671+
pragma solidity ^0.8.29;
1672+
1673+
import "./ExternalLib.sol";
1674+
1675+
contract UseExternalLib {
1676+
// The compiler inserts a reference that must be linked to the ExternalLib deployed address
1677+
function compute(uint256 x, uint256 y) public pure returns (uint256) {
1678+
return ExternalLib.externalAdd(x, y);
1679+
}
1680+
}
1681+
```
1682+
1683+
- **Deployment**: You must first deploy `ExternalLib` and then link its address into `UseExternalLib`’s bytecode.
1684+
- At runtime, calls to `ExternalLib.externalAdd` happen via DELEGATECALL.
1685+
1686+
## Library for Struct Extensions
1687+
1688+
- A popular pattern is to write libraries that extend built-in types or custom structs with `using for`.
1689+
- This adds **library functions** as if they were member functions of the type.
1690+
1691+
```solidity
1692+
pragma solidity ^0.8.29;
1693+
1694+
library ArrayUtils {
1695+
function findIndex(uint256[] storage arr, uint256 value) internal view returns (int256) {
1696+
for (uint256 i = 0; i < arr.length; i++) {
1697+
if (arr[i] == value) {
1698+
return int256(i);
1699+
}
1700+
}
1701+
return -1; // Not found
1702+
}
1703+
}
1704+
1705+
contract MyArray {
1706+
using ArrayUtils for uint256[]; // "using for" directive
1707+
1708+
uint256[] private data;
1709+
1710+
function addValue(uint256 value) external {
1711+
data.push(value);
1712+
}
1713+
1714+
function findValue(uint256 value) external view returns (int256) {
1715+
// We can now call findIndex() as if it's a member of data
1716+
return data.findIndex(value);
1717+
}
1718+
}
1719+
```
1720+
1721+
- The `using ArrayUtils for uint256[];` directive allows you to call `data.findIndex(value)` directly.
1722+
- This improves readability and encapsulates utility logic for arrays.
1723+
1724+
## Best Practices
1725+
1726+
- **Keep Libraries Focused**
1727+
1728+
- A library should do **one thing** well—e.g., math operations, array helpers, address utilities, etc.
1729+
1730+
- **Avoid Large External Libraries for Single Contracts**
1731+
1732+
- If you’re only using a library for a single contract and that library has few functions, it might be simpler and cheaper to make them internal.
1733+
1734+
- **Mark Functions `pure` or `view` Where Possible**
1735+
1736+
- Avoid unintentional state modifications in libraries unless that’s the library’s explicit purpose.
1737+
- This also ensures your library is safer and can be reasoned about more easily.
1738+
1739+
- **`using for` Pattern**
1740+
1741+
- Great for readability and a more object-oriented feel.
1742+
- Clarifies which types are extended by the library.
1743+
1744+
- **Security**
1745+
1746+
- Libraries can mutate the caller’s storage if called with `delegatecall`. Ensure your library code is trusted and thoroughly reviewed.
1747+
1748+
- **Solady, OpenZeppelin and Other Standard Libraries**
1749+
1750+
- Before writing your own, check if established libraries (e.g., Solady, OpenZeppelin) cover your needs. This reduces risk and leverages well-audited code.
1751+
15691752
# References
15701753

15711754
- [Solidity Docs](https://docs.soliditylang.org/en/latest/)

0 commit comments

Comments
 (0)