One hundred and sixty one lines of Safe, opened cold. Nothing is introduced, because there is nothing left to introduce: the owners are kept in a mapping whose values are its own keys, and one comment is the only place in the file that says so.
16 steps~30 min3 nodes for your map
01 · Nothing here is new
One hundred and sixty one lines from Safe, and this is the last file in the course you will read this way. There is no preamble to it. Open it the way you would open anything you had never seen before: the state block first, then the functions, then back for the parts that only make sense afterwards.
Nothing in it is new. Every word on these boards arrived in a checkpoint behind you, and where something looks unfamiliar it will turn out to be an arrangement of parts you already own rather than a word you are missing. Read it for what it stores.
02 · The header, and three announcements
Line 3 imports one file and line 12 says `is SelfAuthorized`, so there is a parent, and this checkpoint never opens it. Line 12 also carries `abstract`, which settles that whatever this file is, it arrives as part of something larger and never deploys on its own.
Lines 13 to 15 announce three events with no bodies, two of them carrying `indexed`. Read the names rather than the shapes. Something is added, something is removed, and a number changes. That is the whole of what this file does, before you have read one function.
03 · Three variables and a constant
Line 17 fixes an address as a `constant`, so it lives in the code rather than in storage. Lines 19 to 21 are everything this contract keeps: one mapping and two `uint256` counters whose names say what they count. Three storage variables for a whole file.
Line 19 is the one to stop on. It takes an address and answers with an address, which is not a shape you have seen a mapping wear before. Every other mapping in this course answered with something of a different kind. Decide what this one hands back.
Line 19 takes an address as its key and answers with an address. Before you read another line, what would you expect `owners[someAddress]` to hold?
04 · A value chosen for not being one
Line 7 is the only place in one hundred and sixty one lines where the arrangement is named out loud, and it sits inside a comment, which means the compiler never reads it. Take it as a hint to be checked against the code, not as a fact you have been handed.
Line 17 is the other half of it. `address(0x1)` is a real `address` value and is not a real account, which is exactly the point: it can sit in the mapping as a marker without ever colliding with somebody's own address. It is also not zero, and lines 41, 60 and 82 keep the two apart deliberately.
05 · Codes instead of sentences
Line 28 is `internal`, so nothing outside can reach it, and the three lines after it are `require` with a second argument that is not a sentence. GS200, GS201, GS202. That is a house convention rather than a language feature: `require` takes any string it is given, and this codebase chose short ones.
Read them as what they refuse. Line 31 passes only while `threshold` is still zero, which is true exactly once. Line 33 wants the number no larger than the array and line 35 wants it at least one. The same codes turn up again on lines 80, 121 and 123, which is how you learn what each one means.
06 · Stitching, one owner at a time
Line 37 puts the constant from line 17 into a local variable, and the loop opening on line 38 runs once for every address in the array. Line 40 takes one out, line 41 refuses on four separate counts, and line 43 refuses an address that already has an entry in the mapping.
Then the two lines that build the thing. Line 44 writes the new owner underneath whatever `currentOwner` is holding, and line 45 moves `currentOwner` on to the owner just written. Every pass therefore writes a different key, because line 45 changed it. Read those two lines three times over with three addresses.
07 · The line after the loop
Line 47 runs once, after the loop has finished, and at that moment `currentOwner` is still holding the last address the loop touched. It writes the constant from line 17 underneath that address. Lines 48 and 49 then set the two counters, and `setupOwners` is finished.
So the constant is used twice over: once as a key, by the first pass of line 44, and once as a value, by line 47. Before you scroll any further, say out loud what sits under the constant and what sits under the last owner.
08 · In at the head, in two lines
The board has moved to lines 52 to 90. Line 58 is `public` and carries `authorized`, a modifier declared nowhere in this file. Line 3 and line 12 say where it comes from, and reading what it checks would mean opening that file, which this checkpoint does not do.
Lines 63 and 64 are the whole of the work. Line 63 puts whatever the constant currently points at underneath the new owner, and line 64 points the constant at the new owner. Run those two in the other order and the first address is lost, because line 64 would have written over what line 63 needed to read.
09 · The argument you have to supply
Line 78 takes three arguments and the first of them is `prevOwner`. Nothing else in the file asks for anything like it. Line 74 says what it means: the owner whose entry points at the one being removed. The caller has to work that out beforehand and pass it in.
The reason is line 19. Each address holds the next one and nothing holds the previous one, so starting from a single owner this contract can walk forwards and cannot walk back. Line 83 is where the caller's answer is marked: `owners[prevOwner]` must be the owner named.
10 · Removing, in three writes
Line 80 refuses the call unless the count less one is still at least the number being asked for. Line 82 keeps the owner argument away from zero and away from the constant. Line 83 is the pairing check from the last step, and once it passes the three writes run.
Line 84 copies what the removed owner was holding into the entry above it, so the run reaches past it. Line 85 clears the removed entry back to zero, which is what line 141 later reads. Line 86 drops the count. Now take a concrete run and work one call through it.
11 · Out and in, at the same place
Lines 92 to 126 now. `swapOwner` asks for `prevOwner` for the reason `removeOwner` did, checks the new address the way line 60 did, and marks the pairing the way line 83 did. Four requires in a row, and then three writes on lines 107 to 109.
Line 107 gives the new owner whatever the old one was pointing at. Line 108 points the entry above at the new owner. Line 109 clears the old entry. Notice what is absent: `ownerCount` is never touched here, because one address went out as one came in and the length did not move.
12 · What the number is checked against
Line 119 is `public` and carries the same modifier, so it can be called on its own, and lines 68 and 89 also call it from inside the two functions above whenever the number they were handed differs from the one already stored. One function, reachable two ways.
Line 121 wants the number no larger than `ownerCount` and line 123 wants it at least one. That is the entire relationship between the two numbers in this file. Line 124 stores it and line 125 announces it, reading the stored variable back out rather than passing the argument along.
13 · A membership test that never walks
The last board, lines 128 to 161. `getThreshold` hands the stored number back and there is nothing else to say about it. `isOwner` on line 140 is the one worth reading, and it answers in a single expression with no loop anywhere inside it.
Line 141 asks two things: that the address is not the constant, and that its entry is not zero. Every owner holds a nonzero address because lines 44, 47, 63 and 107 all wrote one there, and lines 85 and 109 are the only two that ever put zero back.
14 · Walking the whole run
`getOwners` is the only function in the file that visits every owner, and it takes eleven lines to do what `isOwner` did in one. It builds an array in `memory`, walks from the head as far as the mark, and hands the array back on line 159.
Two things run alongside each other here and neither one drives the other: a position in the array, and a place in the run. Tap each of the five lit lines in turn and read what it contributes to the walk.
15 · The whole file, at once
Every line on one board with the comments dimmed away. One constant, one mapping, two counters, three events, one function that sets up, three that change and three that read. Eighty lines of code inside a file of one hundred and sixty one, and you have read all of them.
Nothing in it was new. A mapping, a constant, a loop, a `require`, a modifier from a parent, an array in `memory`. The arrangement was unfamiliar and every part of it was not. Before you leave the file, be exact about what it has settled.
16 · The reading half, closed
Eleven weeks ago you read a twenty four line contract with every word explained before you met it. This one was handed to you whole, and the single thing in it you had not seen before was an arrangement of things you had. Nobody told you in advance that it was there.
That is the whole of what reading is. Not knowing every file, which nobody does, but being able to open one and work it out from the state block down. The act that follows turns the same method onto code a machine writes for you.
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.
ownerless ledger
you?
nobody owns the table. so who owns your row?
Three new nodes on your map
runs in a mapping · sentinels · reading cold · +10 Lynx