The Grammar · Contracts Talking to Contracts · Week 7 · Checkpoint 15
The Call Raw
Every call you have read so far looked like a function call. Read the library that does them by hand, one hundred and sixty seven lines in seven windows, and a call turns out to be an address, a run of bytes, an amount of wei, and an answer that is also a run of bytes.
15 steps~30 min3 nodes for your map
01 · The file under the syntax
You have read `token.transfer(to, value)` and `super.supportsInterface(id)` and thought of them as function calls, because that is what they look like. Underneath, the machine has no notion of a function. It knows an address, a run of bytes to hand over, and an amount of wei to send along.
This OpenZeppelin library is one hundred and sixty seven lines about exactly that, and it arrives in seven windows numbered the way the file numbers itself. The first sixteen are paperwork and one error.
02 · Two files arrive before anything happens
A licence comment, a release stamp, a pragma with a ceiling on it. You have read this opening several times now and can skim it in a second. The two lines worth stopping on are 6 and 7, and both are imports of the shape you already know.
`Errors` is a set of error declarations shared across the whole library. `LowLevelCall` is where the actual machine instruction lives. This file is the readable wrapper around that one, which is why a prefix keeps appearing in front of the real work.
03 · One error declared here, two arriving from elsewhere
Line 12 opens a library, which you read in the last checkpoint: no state of its own, nothing to deploy, functions meant to be called against a value you are already holding. Every function in here is `internal`, so nothing outside can call one: a contract using this library carries these lines around inside itself.
Line 16 declares a custom error, and the comment above it says the whole of it: there is no code at `target`. Before the file is over you will watch it revert with three different error names. Only one of them is on this board.
This file reverts with three error names in total, and line 16 declares one of them. Where do the other two come from?
04 · Sending ether is its own problem
In checkpoint one you read `msg.sender.transfer(wad)` and it looked like the simplest line in that file. It sends wei to an address, and for years it was the ordinary way to pay somebody out. Line 19 calls the function below this comment a replacement for it.
Line 20 names the difference in five words: forwarding all available gas. A plain `transfer` forwards a fixed, small allowance and nothing more. That allowance is a number, and numbers in this machine have been changed before.
05 · Twenty three hundred, and a repricing
Here is the story, told in the comment. The allowance `transfer` forwards is two thousand three hundred gas. An upgrade raised the price of certain operations, and a receiving contract whose code used to fit inside that allowance could stop fitting.
Nothing about the receiver had changed. The price list had. So this library offers a send that forwards everything instead of a fixed allowance, and current code reaches for it while older code you read still says `transfer`. Lines 29 to 32 then send you elsewhere entirely, which is what a good comment does when the answer is not in this file.
06 · What it asks before it sends
Line 34 takes an `address payable` and a `uint256`, returns nothing, and is `internal`. Lines 35 to 37 are one check: if this contract holds less than the amount, refuse now. The refusal carries both numbers, what the balance actually is and what was asked for.
`Errors.InsufficientBalance` is the prefix you predicted three steps ago, in the flesh. Nothing has been sent yet. The sending is line 38, one call with three arguments, and not one of those arguments is a function name.
07 · One send, and three endings
Line 38 is the send. It hands the recipient the amount and an empty run of bytes, and what comes back is a `bool`: did the call finish without reverting. There is no function name anywhere in that line, because at this level there is no such thing as a function name.
If the answer is yes, line 40 returns and the work is done. If it is no, two lines decide which refusal you get. Tap the three lit lines and read what each one is deciding.
08 · Bytes in, bytes out
Line 66 is the function everything else in this file is a variation of. It takes an address and a `bytes memory`, and it hands a `bytes memory` back. Function first: a run of bytes whose length is decided while the program runs, held in memory rather than storage. The name: `bytes memory`.
You met `bytes4` in checkpoint thirteen, four bytes wide and never any other number. This is the other kind. Line 58 calls what comes back the raw returned data, and line 59 names the one thing you can do with it.
09 · What the comment promises
Lines 53 to 56 promise behaviour you have already watched happen. If the other side reverts and says something, this function repeats it. If it reverts and says nothing, you get `FailedCall` instead. The same two branches you read in `sendValue`, one function later.
Lines 63 and 64 list two requirements. The second one is plain enough. The first is the one to hold on to: `target` must be a contract. Line 66 does not check that before calling, and the reason is on the next board.
10 · The call itself, in one line
Line 79 is the same function with an amount of wei added, and lines 80 to 82 repeat the balance check you read in `sendValue`. Then line 83: hand `target` the `value` and the `data`, and keep one `bool`. That line is a call, complete.
The name says no return, and it means the bytes have not been copied anywhere yet. They are sitting where the machine left them. Four branches follow, and between them they cover every way this can have gone.
11 · Success, and nothing there
Line 84 is one `and` holding an `or`: the call came back true, and either it left bytes behind or there is code deployed at `target`. Function first: the bytes deployed at an address, with `.length` counting them exactly as it counts an array. The name: `code`.
Line 87 is the branch that names this checkpoint. The call succeeded, nothing came back, and nothing is deployed there. The machine ran that call anyway, because an address is twenty bytes and it never promised anything was behind them. This library checks. The machine does not.
12 · Two variants, named and left
Lines 100 to 109 and 117 to 126 are lines 83 to 92 over again, twice, with one name changed in each and four branches underneath that are word for word what you just read. Function first: a call the machine will not allow to write anything. The name: a static call, and it is the swap on line 100.
Line 99 says as much in the signature, where the whole function is `view` and line 116's is not. The other swap, on line 117, is a delegate call. What that one changes is large enough to be a checkpoint of its own, and it is the one about proxies.
13 · The pair, before it was split
Line 136 is the same logic with the call already made somewhere else. It takes the `bool` and the `bytes memory` as two parameters, and that pair, `success` and `returndata`, is the shape most code you read still uses. Line 134 says this version is on its way out.
Compare line 143 with line 84. `returndata.length` here, where the newer one asked the machine for a size. The same question, asked of a value you are holding rather than of the call you just made. The branches under it are otherwise identical.
14 · Nine lines, and a word from checkpoint thirteen
The final function takes the pair and nothing else. There is no address here, so it cannot ask whether code is deployed anywhere, so it drops the branch that needed to. Three outcomes are left: hand the bytes back, repeat the reason the other side gave, or say `FailedCall`.
Line 158 is `pure`, one notch stricter than the `view` on line 140, for the reason you read in checkpoint thirteen: this body reads nothing from the chain at all. Which leaves one question about the `bool` it is handed.
15 · One hundred and sixty seven lines, one shape
Seven windows, and the same handful of lines four times over: make the call, ask what came back, hand it over or name the refusal. `sendValue` sends wei with no data, `functionCall` sends data with no wei, and the two variants change one word in the middle.
The typed call you have been reading all course is the compiler writing those bytes for you and reading the answer back. Underneath it, every call is this.
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.