The Grammar · Contracts Talking to Contracts · Week 6 · Checkpoint 14
Calling a Stranger
A hundred and twenty lines that are not a contract, do not deploy, and exist for one reason: a token may hand back nothing, and nothing is not the same as no. Read the library, then decide where to stop.
14 steps~26 min3 nodes for your map
01 · A file you will use without ever holding it
Your contract is holding an `IERC20`. Behind that address is a token somebody else wrote, whose code you have not read and cannot open from inside your own. Before you move any of it you reach for this file, and the first thing worth noticing is that the word `contract` never appears in it.
A hundred and twenty lines, and none of them will ever sit at an address you can point at. Working out what that sentence means is most of the checkpoint. The last beat is a window further down the same file, where the language changes.
02 · Three imports and a path that names the job
A licence, a release stamp, and a pragma with a ceiling above it, because a file with working code in it wants one. Read the path on line 2 as well: `utils`, sitting under `token/ERC20`. This is filed as a helper for the standard rather than as a piece of it.
Three imports follow. Line 8 brings in `IERC20`, the interface your own code already carries. The other two name interfaces this file can also work with. Nothing imported here is a contract either, which is starting to be a pattern rather than a coincidence.
03 · The word standing where `contract` would be
Line 19 opens the body, and the keyword in front of the name is `library`. Every file you have read so far opened with `contract`, `abstract contract` or `interface`. This is the fourth kind of thing a `.sol` file can declare, and it does not behave like any of the three.
You know what a contract carries: stored values, a constructor, an address once it is deployed. Decide which of those a library keeps before reading another line. Scan down from 19 for what is missing as much as for what is there.
Line 19 says `library` where every file so far said `contract`. What does that word change?
04 · Attaching a library to a type
Lines 16 and 17 tell you how the file is meant to be used, and they do it in the doc comment rather than in code. You add one statement to your own contract, `using SafeERC20 for IERC20;`, and after that, line 17 says, you get to write `token.safeTransfer(...)` instead of naming the library every time.
Now hold that against line 34. The function declared there takes the token as its first parameter, and the notation on line 17 does not appear to pass one anywhere. Work out where that first argument went before the file tells you.
05 · Two errors, and what each one carries
Custom errors you already read: a name, a list of values, declared once and thrown with `revert`. Line 23 is the general one and carries exactly one `address`, the token the operation was aimed at. Line 21 above it says what it means in six words.
Line 28 is narrower and carries three values: the spender, the allowance that was actually there, and the decrease that was asked for. Carrying the token address matters because your contract may hold several of them, and a failure that does not say which one is a failure you cannot do anything about.
06 · The first entry point, and where its code ends up
Line 34 declares `safeTransfer` as `internal`, and in a library that word decides something specific. Internal functions are not reachable from outside and nothing is deployed on their behalf. The compiler folds their code into whichever contract uses them, so your own contract ends up carrying it.
The body is one `if`. Line 35 hands the work to `_safeTransfer` with a fourth argument set to `true`, and if what comes back is false, line 36 reverts with the error from line 23. Everything interesting is behind that helper name, and the helper is a long way down the file.
07 · The sentence the whole library exists for
Lines 12 to 15 and lines 31 to 32 say the same thing in different words. A token's `transfer` may hand back a `bool`. It may also hand back nothing at all, because plenty of tokens were written before the shape settled, and an interface describes what your side expects, never what the other side does.
So a call can come back with a `false`, with a `true`, or with no return data whatsoever. A call that returns nothing is not the same as a call that failed. Decide what this library does with that third case before reading the sentence again.
08 · The same work, reported two ways
Line 53 declares `trySafeTransfer`. It takes the same three values line 34 takes and adds `returns (bool)`. Line 54 calls the same private helper with the same three values, and exactly one thing has changed: the fourth argument is `false` where line 35 passed `true`.
So this is not two implementations of one job. It is one implementation and a switch. One of the pair reverts when the work does not succeed, the other hands you a bool and lets you decide. The switch has a name, and you will read it in a moment.
09 · Four doors, two helpers, one boolean
Lines 34 to 62 declare four entry points and they share two private helpers between them. Two of the four revert when the work does not succeed and two hand back a bool, and the whole of the difference inside each pair is the last argument on a single line.
Line 45 is the `From` variant of line 35 and passes `true`. Line 61 is the `From` variant of line 54 and passes `false`. The name on each line tells you which job is being done, and the last argument tells you what happens when it does not succeed. Tap the three lit calls and read what each one is choosing.
10 · A function calling itself out of a corner
Line 106 is `forceApprove` and its body is three calls to one private helper. The first, on line 107, passes `false`, so a refusal comes back as a bool rather than stopping the transaction. When it does come back false, the function does not give up.
Lines 108 and 109 try again in two steps, setting the allowance to zero first and then to the value, this time with `true` so a second refusal does stop everything. Lines 99 and 100 say why: some tokens require the zero step. Same boolean, used here to make a second attempt possible at all.
11 · The read back, and where the window stops
A licence, a pragma, three imports, then a library with no state inside it. Two errors, seven internal functions, and a doc comment above each one carrying what the code itself cannot say. Everything you have read attaches to your own contract through the single statement on line 16.
Line 120 opens one more entry point and the window stops before its body. The file runs to two hundred and ninety two lines, and the part that does the actual calling is a long way below, written in a shape you have not met.
12 · A different language inside the same file
Here is the helper the four entry points have been calling. Line 188 declares it `private`, so nothing outside the library reaches it, and the fourth parameter finally has a name: `bubble`. That is the switch you have been reading all along, and line 186 is the doc comment that explains it.
Line 189 is a `bytes4 selector`, and that one you can read on sight: four bytes naming the function this helper is about to reach for. Then line 191 says `assembly ("memory-safe")`. Everything under that brace is a different language, and its words are `mload`, `mstore` and `call`.
13 · Deciding a file has told you enough
This is assembly and it belongs to a later checkpoint. You can still say what these lines are for, because the comment above them already said it: imitate `token.transfer(to, value)`, treat the returned value as optional, and act on `bubble` if the call reverts. The file told you, in its own words, above the code.
That is a real reading. Seven entry points traced to two helpers, one boolean found between each pair, and a clean stop at the line where the language changed. Now say exactly what this file has and has not told you about the token at the other end.
14 · Two keywords, and one decision
Two words carried this whole file. `library`, which is a place to keep functions that no contract owns and nothing deploys on their own. And `using SafeERC20 for IERC20;`, which puts those functions within reach of a type you were already holding. Neither one puts anything new on chain.
There was a third thing, and it is not a keyword. You decided where to stop reading. The doc comments told you what the assembly is for, you confirmed the shape around it, and you left the rest to the checkpoint that teaches it.
BANK_DBowner: the bank
you2,400
what the app is actually showing you
BANK_DBowner: the bank ✍
you2,400their pen
you hold a claim. they hold the pen.
your digital life
BANK · you2,400the bank ✍
INSTAGRAM · you2.1M followersMeta ✍
STEAM · you134 gamesValve ✍
AIRLINE · you58,200 milesthe airline ✍
four tables. zero pens that are yours.
BANK_DBowner: the bank ✍
you2,400
DENIED ✗
try both pens
PLATFORM_DBowner: the platform ✍
her · 8 years2,000,000 followers
one automated decision away
your row stands on all three
FTX_DBowner: FTX ✍
you5 BTC
the row stayed. the backing did not.
CARD_DBowner: your bank ✍
TV you never bought−1,100
fraud reversal+1,100 ✓
someone holds the pen, so someone can fix it
?_DBowner: nobody
youstill yours?
can a table exist that nobody owns?
?_DBowner: ̶n̶o̶b̶o̶d̶y̶
you100
no owner, no pen, no trust?
keeper 1
you100
keeper 2
you100
keeper 3
you100
keeper 4
you100
keeper 5
you100
no THE copy, only copies.
keeper 2
you100
keeper 3
you100
keeper 4
you100
keeper 5
you100
your copy
you100
five copies. one of them is yours.
one attacker, ten thousand faces.
writing costs watts. faking voters buys nothing.
proof of work, burn energy to vote.
rewrite one line, break every lock after it.
the price buys trustlessness. the office already has trust.