You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
These are lower-level functions than the usual contract function calls because they bypass the Solidity function name → selector → ABI encoding process (unless you manually encode/decode arguments and return data). They return success/failure booleans and raw byte data rather than automatically reverting on failure or decoding data.
2231
+
2232
+
### Why Use Low-Level Calls?
2233
+
2234
+
1.**Dynamic function calls**: If you only know at runtime which contract or function signature you’re calling.
2235
+
2.**Proxies**: Common pattern for upgradeable contracts or decoupling logic from storage.
2236
+
3.**Manual gas management**: You can specify the exact gas or handle reverts manually.
2237
+
2238
+
## `call`
2239
+
2240
+
`call` allows you to call a specified address (contract or EOA) with custom calldata, value (Ether), and an optional gas parameter.
-**Does Not Auto-Revert**: If the call fails (e.g., the target reverts), `success` will be `false`. The state changes in your contract up to that point remain unless you manually revert.
2249
+
-**Returns Raw Data**: `returnedData` is the raw bytes the target contract returned. You must decode it if you expect a specific type (e.g., an integer or a boolean).
2250
+
-**Forwards Gas**: By default, `call` will forward all remaining gas. You can specify a `gas: someAmount` to limit it.
2251
+
-**Potential Reentrancy**: Because you may be forwarding a lot of gas, be mindful of reentrancy vulnerabilities if your contract’s state is updated before calling out.
2252
+
2253
+
```solidity
2254
+
function callTransfer(address _token, address _to, uint256 _amount) external {
2255
+
// Encode the function selector and arguments for an ERC20 transfer
`delegatecall` is similar to `call` but crucially **executes the code of the target contract in the context of the caller’s state**. This is the foundation for **proxy** contracts or **upgradeable** contract patterns.
-`msg.sender` and `msg.value` remain the same as in the original call that reached the caller.
2284
+
- Any storage changes (writes) made by the executed code affect the caller contract’s storage layout.
2285
+
-**No Ether Transfer**:
2286
+
- Unlike `call`, you can’t directly send Ether with `delegatecall`. It only executes code with the current call’s context.
2287
+
-**State & Storage Layout**:
2288
+
- You must ensure the storage layout of the caller and the target match if the target code writes to storage. Mismatched layouts lead to corruption or undefined behavior.
2289
+
-**Returns**:
2290
+
- Similar to `call`, you get `(bool success, bytes memory returnData)`. You must handle them manually.
2291
+
2292
+
```solidity
2293
+
contract Proxy {
2294
+
address public implementation; // Points to logic contract
2295
+
2296
+
constructor(address _impl) {
2297
+
implementation = _impl;
2298
+
}
2299
+
2300
+
fallback() external payable {
2301
+
// Forward all calls to 'implementation' using delegatecall
`staticcall` is another low-level function used for read-only calls. It reverts if the called code attempts to modify state (like writing to storage or emitting events).
|**State Context**| Runs in the **target’s** context. Writes in the **target’s** storage. | Runs in the **caller’s** context. Writes in the **caller’s** storage. |
2326
+
|**msg.sender / msg.value**|**msg.sender** is the caller (the contract executing `call`). |**msg.sender** remains the original external address or higher-level caller. |
2327
+
|**Ether Transfer**| You can send Ether along with it (`value: X`). | No direct Ether transfer is possible. |
2328
+
|**Common Use Case**| Directly calling external contracts, optionally sending Ether. | Proxy patterns, libraries, or upgradeable contracts (code is borrowed but state remains in the caller). |
2329
+
|**Gas Forwarding**| Forwards all remaining gas unless specified otherwise. | Same, but in the caller’s context (no Ether). |
2330
+
2331
+
## Security & Best Practices
2332
+
2333
+
1.**Check Return Values**
2334
+
2335
+
- Both `call` and `delegatecall` return a boolean indicating success/failure. Always check it.
2336
+
2337
+
2.**Handle returnData**
2338
+
2339
+
- If the called function returns data, decode it if you care about it.
2340
+
- If the call reverts with a reason string, that’s included in `returnData`. You can bubble it up using inline assembly or revert with your own error.
2341
+
2342
+
3.**Protect Against Reentrancy**
2343
+
2344
+
- If you use `call` to forward large amounts of gas, your contract can be reentered. Use patterns like **Checks-Effects-Interactions** or **ReentrancyGuard**.
2345
+
2346
+
4.**Proxy Storage Layout**
2347
+
2348
+
- With `delegatecall`, ensure the **implementation** contract’s storage layout is compatible with the proxy contract’s layout. If you add new variables in the implementation or reorder them, you can break or corrupt the proxy’s storage.
2349
+
2350
+
5.**Avoid Arbitrary Delegatecalls**
2351
+
2352
+
- Letting users choose any target for `delegatecall` is a critical security hole. Restrict target addresses or function signatures if you want safe upgrade patterns.
2353
+
2354
+
6.**Gas Estimation**
2355
+
2356
+
- Low-level calls might confuse the Solidity gas estimator. You sometimes need to manually specify gas or test thoroughly to avoid out-of-gas issues.
2357
+
2214
2358
# Create, Create2, Create3, and CreateX
2215
2359
2216
2360
# ABI Encode & Decode
@@ -2227,7 +2371,3 @@ contract WontCompile {
2227
2371
-[Solidity Gas Optimization Techniques: Loops](https://hackmd.io/@totomanov/gas-optimization-loops#Solidity-Gas-Optimization-Techniques-Loops)
2228
2372
-[Gas Optimization In Solidity: Strategies For Cost-Effective Smart Contracts](https://hacken.io/discover/solidity-gas-optimization/)
2229
2373
-[Understanding the Function Selector in Solidity](https://www.rareskills.io/post/function-selector)
0 commit comments