Skip to content

Commit 510d5c7

Browse files
authored
Merge branch 'master' into dargon789/gamefi
2 parents 2610e6d + 2bdb531 commit 510d5c7

6 files changed

Lines changed: 796 additions & 0 deletions

File tree

crates/lint/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ It helps enforce best practices and improve code quality within Foundry projects
5555
- `could-be-immutable`: Recommends declaring constructor-only state variables as `immutable`.
5656
- `could-be-constant`: Recommends declaring never-written state variables with a compile-time-constant initializer as `constant`.
5757
- `custom-errors`: Recommends using custom errors instead of strings and plain reverts for potential gas savings.
58+
- `external-function`: `public` functions never called internally should be declared `external` to avoid copying reference-type arguments into memory.
5859
- `unused-state-variables`: State variables that are never used should be removed.
5960
- `var-read-using-this`: Reads of state variables (or other `view`/`pure` functions) via `this` cause an unnecessary `STATICCALL`; access them directly.
6061
- **Code Size:**
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
# External Function
2+
3+
**Severity**: `Gas`
4+
**ID**: `external-function`
5+
6+
`public` functions that are never called from inside the contract (or any of its
7+
derivatives) can be declared `external`. External functions read their reference-type
8+
arguments directly from `calldata` instead of copying them into `memory`, which saves
9+
gas at every call site.
10+
11+
## What it does
12+
13+
Flags a `public` function declaration when **all** of the following hold:
14+
15+
- The function is `public` (not `external`, `internal`, or `private`).
16+
- It is an ordinary function (not a constructor, fallback, receive, or modifier).
17+
- It has at least one parameter that is a reference type (`struct`, array, `bytes`, or
18+
`string`) currently located in `memory`.
19+
- It is not an `override` of another function (the base must be migrated first).
20+
- It has a body (not abstract or interface-only).
21+
- It does not write to any of its parameters inside the body.
22+
- It is never called from inside the contract or any contract that derives from it,
23+
whether directly (`foo()`), via `super.foo(...)`, or via a function-pointer reference
24+
(`fn = foo;`).
25+
26+
The lint runs in the `Gas` severity bucket and is automatically skipped on Foundry
27+
test and script files.
28+
29+
## Why is this bad?
30+
31+
Calling a `public` function from outside the contract is more expensive than calling
32+
the equivalent `external` function:
33+
34+
- Each reference-type parameter is copied from `calldata` into `memory` before the
35+
function body executes, even though `external`-only callers never need that copy.
36+
- The opcode shim that allows the function to be called both internally and externally
37+
adds a few bytes of bytecode and an extra branch on every entry.
38+
39+
When the function is never called internally, switching `public` to `external` removes
40+
both costs at no semantic change.
41+
42+
## Example
43+
44+
### Bad
45+
46+
```solidity
47+
contract Vault {
48+
mapping(address => uint256) public balances;
49+
50+
function deposit(address[] memory accounts, uint256[] memory amounts) public {
51+
for (uint256 i = 0; i < accounts.length; i++) {
52+
balances[accounts[i]] += amounts[i];
53+
}
54+
}
55+
}
56+
```
57+
58+
`deposit` is never called from inside `Vault`, but its `memory` arrays force an
59+
unnecessary calldata-to-memory copy on every external call.
60+
61+
### Good
62+
63+
```solidity
64+
contract Vault {
65+
mapping(address => uint256) public balances;
66+
67+
function deposit(address[] calldata accounts, uint256[] calldata amounts) external {
68+
for (uint256 i = 0; i < accounts.length; i++) {
69+
balances[accounts[i]] += amounts[i];
70+
}
71+
}
72+
}
73+
```
74+
75+
When you migrate `public` to `external`, also change reference-type parameters from
76+
`memory` to `calldata` to capture the full gas saving.

0 commit comments

Comments
 (0)