Jamie on Software

Links, June 2022

Found myself in somewhat of a funk this month, low on energy and motivation. My usual menu of neuroses bubbling up and my usual response; fits-and-starts of bouyancy followed by lethargy. Unhappy with my work. I think I need a holiday. And twenty more IQ points.

Anyway, not much writing, just Wallets As Identity, a rough first draft of a chapter from the Ethereum book.

I began five books but only finished one: Feyerabend’s Against Method. I intend to write in more detail about it, because it was both very good and also rather illegible. After some effort, I think I have a clear idea of what he is trying to do, but I can’t see how his arguments don’t collapse into a much broader scepticism. Here’s a Twitter thread with some quotes I enjoyed.

The unabridged edition of Simon Schama’s History of Great Britain series is wonderful – Schama writes so well – and it’s currently included with any Audible subscription, although that will change this month.

A collection of memos ‘written for an internal audience’, mostly business and technology but some politics too. Diaries and letters and other documentary on how the sausage gets made gives you a good sense for 1. the trade-offs involved in absolutely everything, something that is easy to consider intellectually and much harder to feel intuitively, 2. how chaotic and unregimentable progress is a priori, and 3. the sheer variability of approaches and styles of success and failure.

Michael Nielsen writes on effective altruism and his take on its problems providing a moral core for an individual’s life.

A friend pointed me to this Dylan B-side, from the Blood on the Tracks sessions. Lovely and sad and detached. Dylan’s narrator is always at arm’s length from his subject, even when he’s singing about himself.

Two useful reviews of The Future of Fusion Energy, one from Martin Kleppman, the other from the Astral Codex Ten book review contest: the former reviews the book, the latter reviews its content.

Toby Ord on the knowability of the Edges of the Universe; and a fun lecture from Stuart Armstrong on how we might get there.

For a bit of context on the jurisprudential questions that underly the Roe v Wade debates, a chat between Scalia and Breyer.

And here’s a beautiful photograph of a new(-ish; May 2020) impact crater found on Mars:

Impact crater on Mars

8:30pm. July 2, 2022.

Wallets As Identity

This is a very rough first draft of a chapter from my upcoming book on Product Engineering in Ethereum. Any and all feedback welcome!

Technologists use metaphors to make skeuomorphic links, bridging old paradigms to new. The ‘desktop’ and ‘trash can’ and even the keyboard is a form of skeuomorphic link, giving users of a new platform enough conceptual and aesthetic resources to make sense of it, to feel familiar, connected to it. Computers and interfaces are physical, and rely on all the same instinctual responses, the same clusters of predispositions and responses attached to entities in the physical world. So new technologies can co-opt these instincts by creating these skeuomorphic links.

Perhaps the most common example of skueomorphich linkage in crypto is the metaphor of the ‘wallet’. This post argues that it is one of the most misleading.

A quick diversion on the structure of metaphors: metaphors behave by implying certain things about the subject based on the object. Good metaphors are good when the subject does indeed resemble the object in those ways. Good metaphors also have the nice property of illuminating the subject of the metaphor in ways that the reader doesn’t expect. A good metaphor can help reveal the shapes and contours of the subject by making us consider the subject from a new angle. ‘All the world’s a stage’, Jacques soliliquises in As You Like It, and the entrances and exits and performance that this equivalence conjures up help illuminate the human condition, revealing it to be a form of entrapment, confinement to a pre-determined script.

What, then, does the metaphor of ‘wallet’ imply? It suggests a few features:

  1. A wallet holds things

  2. A wallet holds a specific sort of thing, namely financial things (such as credit cards and cash)

  3. A wallet is a physical item, and plays the various roles that physical items do, such as signalling style, wealth, and status

Crypto wallets – what we’ll call ‘accounts’ from now on to avoid confusion – have none of these features. Accounts don’t hold things: accounts represent identity. Accounts aren’t limited to interacting with financial products: they interact with anything representable on the blockchain. Accounts, of course, aren’t physical items: they are an abstraction over an individual on a ledger.

It’s worth stopping at this point and asking why any of this matters. Isn’t it just a shallow, semantic dispute? No. Because as well as being illuminating about the subject, a metaphor can often constrain our understanding of the subject, especially if it becomes the primary mechanism through which the subject is known.

This has happened with the wallet metaphor in crypto, which is why I believe it’s especially important to pay attention to what wallets actually are, how the metaphor shapes our thinking, and how it limits our thinking too. All the world is a stage, maybe, but if it is we still have plenty of freedom to improvise.

Accounts don’t hold things

An account is a pair of public and private keys. A private key is used to generate a public key, and the public key is used to generate an address. So when you see account addresses such as 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 (one of Vitalik’s accounts), what you’re really seeing is the result of passing Vitalik’s private key through the set of cryptographic functions that produce these addresses.

The details of how this works are interesting, but out of the scope of this post. All we need to know for now is that this process is deterministic, in the sense that it returns the same value for the same input, no matter how often you run it, and irreversible, in the sense that it’s very very hard to get the private key from the wallet address.1

So if you know the private key then you can generate the public key, but you can’t (easily) go in the other direction. This is one of the reasons why it’s so important to keep your private key private: your private key is the one piece of information somebody needs to get access to your account. Fortunately, most users don’t have to worry about the content of the private key at all: they can use wallet software that holds the private key in a secure way. For product engineers, you’ll always need to keep in mind that the private key is sacred.

How does this link to the wallet metaphor?

Well, I lied a little: accounts do ‘hold’ something. They hold ether, the native token of the Ethereum blockchain. But this is by communal assent: a group of people, incentivised to follow the same set of rules, collectively agree that a certain amount of ether is owned by a specific account, and another amount of ether is owned by a different account. It’s the collective assent that underwrites the ownership claim, not the account itself.

If you’ve got cash in your wallet, you actually do have cash in your wallet. A portion of the ‘state’ of the money-system is in your pocket. If you’ve got ether in your account, you’ve got a claim on the group for the amount of that ether. It’s a guarantee, secured by cryptography and incentives, that when you decide you want to do something with that ether we’ll all agree that you can.

So I’m also telling the truth: accounts don’t really hold that much at all, and the notion of ‘holding’ your ether and your tokens is a euphemism. Your account is the mechanism of a record of ownership. The state is not in your pocket, it’s ‘over there’.

This might seem a little opaque, so let’s consider some code. One of the clearest examples of how accounts don’t actually hold anything is the ERC-20 token standard.

This standard is the core primitive in DeFi, the basic lego brick upon which we build this cathedral. It offers a consistent interface for smart contracts that behave as a record of ledger. Contracts have to choose to implement it – that’s why it’s a standard, not a rule – but those that do share the same methods.

We can distil the essence of ERC-20 down to only two function signatures:

function balanceOf(address _owner) public view returns (uint256 balance);
function transferFrom(address _from, address _to, uint256 _value) public returns (bool success);

(The actual standard contains more than these two methods, but the rest are for convenience, expedience, and extensions to the functionality, rather than being central to the model of how tokens are represented by contracts that implement it.)

These two functions tell us everything we need to know about a piece of state. What is that piece of state? It’s a mapping from address to the number of tokens owned by that address:

mapping(address => uint256) public balanceOf;

balanceOf is a map – otherwise known as a dictionary, or an associative array – from an owner to the total amount owned. The total amount owned of what? The token that the contract represents.

If we want to find out how much of the token we own, we call balanceOf, passing our address. If we want to send our tokens to somebody else, we call transferFrom, passing our address, the recipient’s address, and the amount we wish to send. The token contract does the work in updating the list it holds.

Think about this carefully: a token is just a list of who owns the token, plus a bit of metadata (such as the token’s name and symbol).

Your account doesn’t ‘hold’ anything. It doesn’t know anything about the token, per se. It just gives you a way of identifying yourself as the owner of some amount of the token. And a token is just a piece of code that understands how to update this balanceOf map.

This is a very simple and powerful idea, because the token contract is able to stipulate its own rules and policies around who can transfer, who can be transferred, and what the balance is.

You could stipulate that only a specific whitelist of addresses should be able to receive the token, for instance, restricting ownership of the token to a known set of individuals. You could exclude certain addresses. You could require a certain amount of ether in exchange for the token, or even a certain amount of another token in exchange for the token. You could even change the amount transferred when somebody transfers it, deducting a fee. In other words, you can implement a monetary policy, controlling how the token enters the market, how it leaves it, and how it flows around between participants.

Accounts aren’t limited

Since the token contract is the mechanism for recording who owns what, and stipulating the rules that govern that ownership, your account isn’t limited to holding financial instruments like cash or credit cards. Tokens can be used to represent basically anything.

Some tokens represent non-fungible assets, things that can’t be exchanged for another asset just like it. You will have heard of NFTs, and NFTs are token contracts much like the ERC-20 contract we just discussed. The biggest difference between ERC-20 and ERC-721 – the basic standard for representing NFTs – is that the parameters of the transferFrom function are slightly different:

function transferFrom(address from, address to, uint256 id) public virtual;

An ERC-721 contract introduces the notion of an id, and requires that the pair (owner, id) is globally unique. So a specific token – that described by the contract and the specific ID the contract defines – is owned by one and only one person.

NFTs are often used for digital art or collectibles, but they can be used to represent ownership of lots more. In time, they may be used to represent ownership of real-world assets: houses, cars, paintings, bottles of wine.

NFTs can also be used for interesting, crypto-native use cases: Uniswap v3, for instance, mints an NFT that represents a deposit to a specific pool within a specific price range. Each deposit has its own custom configuration, and so the pool mints an NFT that represents your specific deposit with your specific settings.

Fungible tokens, too, can be used to represent many more types of things than just those that map to financial value. They can represent governance rights, as they do with the TRIBE token, which grants the right to vote on proposals of the Tribe DAO. (At the time of writing, most governance tokens grant voting rights on a quantity basis: if you have more tokens, your vote represents more of the. But this isn’t the only model for governance design made possible by the ERC-20 token standard). They can also represent other sorts of values, such as carbon credits, or reputation, or points accumulated in a game.

The point is that the ‘wallet’ metaphor suggests a constraint on the sorts of things that can be ‘held’. Since accounts don’t ‘hold’ things, the design space for what sorts of ownership and participation claims they can represent is much, much bigger.

Accounts aren’t physical items

This might seem like a silly point to make, but the fact that accounts aren’t physical items runs a little deeper than the obvious. Accounts aren’t physical items: they are abstractions over an individual, a face you can show to the blockchain.

And this means that your accounts aren’t tied to one place. If you know your private key, you can use your account in whatever interface you want.

This means that different interfaces can display different aspects of the same account. Some interfaces – CHECK: such as what? – put an emphasis on NFT collectibles, a kind of gallery. Others might focus on the purely financial aspects of the account, its deposits and loans and investments. Perhaps an interface could be built around governance, showing the proposals and votes and engagements with protocols. Perhaps another could be built around whatever gaming tokens the account has claims to. And so on.

When you start to consider these sorts of product implications of the fact that accounts aren’t physical items, it makes you realise that the design space for account interfaces is co-extensive – overlapping with – the design space for tokens themselves.

It also pushes the sorts of signalling properties we mentioned earlier elsewhere. Perhaps people do signal with their wallet software – i.e. their account interfaces – a little, using a newer or more feature-complete piece of software to indicate the sort of user they are, the sorts of things they are interested in. But the account itself is a very lightweight thing.

And because it’s lightweight, it can be transported, as we’ve discussed. It can also be ‘hosted’ on-chain, which opens up the possibility of smart contract wallets and multi-signature wallets.

Accounts are identity

So what is an account, then, if not a wallet?

My answer: an account is an identity. It is a way of informing other participants that you are who you say you are. It is a username and password. It is a mechanism to authenticate yourself.

If accounts are identity, then it means you can separate out your identities depending on different contexts of usage. You might use an account to trade, another account to represent your investments, a third to hold your claims to any artwork or collectibles. You might use accounts for different platforms with different risk profiles, or with different privacy considerations. You can separate out your identities in whatever way you choose, isolating your public, on-chain behaviour into different categories. The ownership of a private key gives you full control over what aspect of yourself you wish to show to the world.

Products built on Ethereum need to understand this multiplicity of identities, and implement support for it in a deep way. We’ll see how this works more concretely in other posts. But for now, consider that your identity goes with you wherever you take it. And where your identity goes, so do your claims of ownership.

This, combined with the standardisation of interfaces such as ERC-20 and ERC-721, increases the power of the interfaces we can build substantially. To take a simple example: a tool for listing whatever ERC-20-compatible tokens are ‘held’ by an account will work with any token that meets the requirements of that interface. It’s as if PayPal could support all currencies from day one, with no extra development work.

The wallet metaphor is generally broken, then, and can severely constrain your imagination vis-a-vis what accounts are capable of. For sure, there are ways in which an account can seem like a wallet: when you lose your account, just like your wallet, you lose the money in it; to that extent, the metaphor is true. But it’s true for different reasons. If you lose a physical wallet you’ve actually lost the cash inside it. If you lose a crypto wallet, you’ve lost your ability to prove you are the person who owns the cash.

We are likely to continue using the term ‘wallet’, since that’s what the community uses, and there’s not much marginal benefit in changing this sort of heavily-entrenched meme. I use ‘wallet’ and ‘account’ interchangeably. But I hope this chapter has given you a sense of how accounts actually work, and why the design space for them is an awful lot bigger than a piece of folded leather that you keep in your pocket.

  1. How hard? There’s no better way than guessing at random. Ethereum private keys are 256 bits. Since a bit has two possible states, guessing a 256 bit sequence correctly at random has a chance of 1/2^256. There are ~10^78 atoms in the observable universe, which is roughly 2^260. Account addresses are only 160 bits long, which means that there are multiple possible private keys, but 2^160 is still a very big number, equivalent to a little bit less than the total number of atoms on Earth. 

10:14am. June 27, 2022.

Links, May 2022

May was a fine month with lots of social engagements, less writing than I wanted, but quite a lot of reading. I ran the Edinburgh Marathon, my first marathon ever, in four hours and 33 minutes. I wrote a piece on Decentralisation as a trade-off space.

To prepare for a debate with my friend David, I read two books on the history of housing development: All That is Solid and Municipal Dreams. The latter was very good. The former descended into Tory-bashing in the key of Owen Jones, which might be righteous but is also a little tiresome. I also read bits of Order Without Design, which was truly excellent; it’s good to see urban theory that grounds itself in, and has respect for, economics.

I also read the second volume of Alastair Campbell’s diaries, covering 1997-1999 and the first few salvos of a triumphantly New Labour. The diary format is excellent, since you get an obviously singular perspective as it unfolds. I hadn’t realised quite how little communication mattered in the civil service prior to Blair; lots of Campbell’s agonies involved getting various govt departments to coordinate messaging, routing comms through Number 10. I also had very little idea of how much work went into the Good Friday agreement, or how tenuous it was. Many many chances for it to fall apart. Had Paisley or Trimble or Adams woken up on the wrong side of bed on the wrong morning and the whole thing would have been doomed. From inside government policy seems much more chaotic and stochastic than I had suspected (which might be a reason to be less worried, at least on the margin, about Moloch tendencies.)

I listened to In the Shadow of the Moon while falling asleep most nights, a very thorough set of biographies and history of the Gemini missions, up to Apollo 11.

As for other links, and continuing on the theme of housing, I also read a few good papers worth reading if the subject appeals to you. Anthony Breach’s Capital Cities: How the Planning System Creates Housing Shortages and Drives Wealth Inequality was extremely clear and thorough, UK-specific, and perfect for preparing for an argument with David. The Housing Theory of Everything helps drive home why this matters so much. YIMBY is a moral argument as much as an economic one.

Campbell’s diaries got me on a bit of a New Labour kick, so I watched last year’s excellent series on Blair and Brown and the 13 years of New Labour government. I’ve also been enjoying The Rest is Politics podcast, hosted by Campbell and Rory Stewart.

Dwarkesh Patel wrote a good post on applying the ‘Barbell Strategy’ to everyday life: reframing habit formation and intellectual projects in terms of oscillating between intense focus on one thing and the simplest, lowest-effort thing possible –– which is often nothing at all.

Ken Shiriff is writing some truly excellent, deep work on the technical substrate of the Apollo missions. This is a post on the premodulation processor, the signal combinator and splitter in the command module.

We can now make clocks so sensitive that they can detect the relativistic difference caused by being one milimetre deeper in the Earth’s gravity well.

Facebook open-sourced a logbook documented while building and deploying one of their NLP models. More companies and people should do this sort of stuff.

Finally, I signed a contract with Apress this week to publish a book on Product Engineering on Ethereum. My aim is to raise the relative status of product engineers – those of us who build everything around smart contracts, UIs, tooling, infrastructure – and explore how the unique processing model of Ethereum puts important constraints on the way we build software. I’ll be posting some pieces here as I work through the first draft, so keep an eye out if you’re interested.

3:26pm. June 2, 2022.

Decentralisation is a trade-off space

When you ask a crypto-sceptic what decentralisation means wrt crypto, they offer these sorts of properties:

  1. Many nodes are run across many jurisdictions; computation happens in a way that can’t ever be shut down or censored
  2. Individuals can access the system for whatever nefarious purposes they wish, without the need for inter alia KYC checks
  3. Power is completely diffused rather than concentrated in any one party; therefore, no individual party is responsible, either legally, operationally, morally, or all of the above.

These sorts of conditions actually refer to what I’ll call ‘maximal decentralisation’ – a system which is decentralised in every way possible, up to whatever point of diminishing marginal returns seems appropriate. It is different from decentralisation, which refers to a system that can have more or less participation, more or less accessibility, more or less diffused power.

This might seem like an unimportant semantic distinction, but not all semantic distinctions are meaningless, and some are indeed important. How we define our terms matters, because it limns the shape of what we build and gives us criteria against which we evaluate our success. Moreover, if we disagree over what we mean by a term like ‘decentralisation’, we end up arguing fruitlessly. We exchange claims and both sides miss the point.

A lot of crypto people also believe, somewhat reflexively, that we should be aiming for maximal decentralisation. The worst crypto-fanatics are just as ideological as the worst crypto-sceptics. This is a problem. People shouldn’t be fanatical.

For almost every category, maximalism is bad. It ties you into a priori commitments, which reduce your optionality and cripple your ability to adjust to new facts. Strong beliefs are important, but they should generally be held weakly. It also makes you much more likely to act tribally. Humans are tribal, and ‘decentralise everything’ can become a rallying cry, a standard around which troops array for battle. It makes technical questions political, and politics is the mind-killer.1

Crypto people shouldn’t be fanatical, because computing is the art of the science of trade-offs, and ‘decentralisation’ gives us a lot more room for manoeuvre than either its critics or fanatics seem to allow. Trade-offs are useful, and understanding the trade-off space is most of the work.

I know that crypto people don’t actually want decentralisation per se, because when you ask why decentralisation matters, you get answers like:

It makes the technology more accessible and more censor-resistant. It gives broader access to a broader set of people, especially those less well-served by existing financial infrastructure.

Which means that their motivations are prior to decentralisation. They want accessibility! They want censorship-resistance, and greater equality of opportunity!

We also want other things. Decentralisation means more competition, and more competition means more innovation. (Diversity is good! Let a thousand flowers bloom!) The ideas being generated and built upon by the crypto community are not commodities: they are meaningfully new contributions to a meaningfully new technology stack; we get more ideas when we have a more competitive space, and we get more competition when people are able to use the technology and gain access to the liquidity needed to prove the new ideas out.

More subtly, another important corollary of decentralisation is that it leads to implicit coordination through standards. And standards make composability possible. So decentralisation is also good because it leads to more composable technologies.

So how then can we think about decentralisation in a more yielding way? How can we make it more supportive of our broader aims – accessibility, censorship-resistance, competition, composability – without holding ourselves hostage to its demands, on the one hand, or giving our opponents a stick to beat us with, on the other?

First, we must realise that decentralisation is a spectrum. It isn’t a binary state. Systems can be more or less decentralised, and what we’re actually arguing over is how decentralised a given system should be.

Second, we must realise that there are different sorts of components in a web3 system, and decentralisation doesn’t just apply to the computation. There are the RPC nodes, the API interface into the computation layer: they can be more or less decentralised. There are frontends, which serve user-friendly interfaces to interact with the contracts via these RPC nodes: they can be more or less decentralised. There is the data itself, the state, which can be more or less decentralised. There is the liquidity, the value locked in the system, which can also be thought of as more or less decentralised. Etc etc etc.

Third, we must realise that how decentralised a given system should be depends on the purpose of that system. A platform that needs to perform a lot of complicated computation can trade off a bit of decentralisation by pulling some computation off-chain.2 Many web3 frontends use the semi-decentralised The Graph to provide indexing, trading off some centralisation in exchange for faster data retrieval. Almost no web3 frontend uses decentralised alternatives to the substrate of internet technologies on which they sit, DNS and web hosts and friends. This is all okay.

Fourth, we must realise that often just the ability to decentralise means that the goals of decentralisation can be achieved. In other words, centralised parts of a decentralised system don’t immediately make the system totally centralised – it’s not a binary – and therefore useless vis-a-vis the goals of decentralising. This is one of the reasons why moxie is wrong: while it’s true that there are a few dominant hosts of RPC nodes, such as Alchemy, it is very easy to run nodes, and would not be especially capital intensive to start competitors.3 Alchemy won’t abuse their power, because if they did it would be extremely easy to compete with them. The fact that the computation layer and its implementation – e.g. the geth codebase – is permissionlessly accessible means that there are downstream pressures on people who use those implementations to not abuse their power. ‘Counterfactually decentralised’ is a real thing!

Finally, and most importantly, we must realise that we have a choice, as builders and consumers, and that the truest expression of a decentralised system is one in which individuals are given freedom to engage (in the broadest possible sense) with the project (in the broadest possible sense) –– on their own terms. On this count, at least, crypto seems to be doing well, even though aspects of it are indeed centralised.

My point is that crypto-fanatics and -sceptics alike seem to think that the fight is over whether decentralisation is ‘necessary’, which is about as helpful as asking whether a bridge is strong enough, or a cow is fat enough, or a child is confident enough.

Necessary for what?

  1. I hope to write more about the relationship between fact-questions and values-questions in the future. I feel intuitively and reflexively annoyed by the post-modern adage that everything is political, because grounding facts-questions in values removes our one objective standard, our one ability to offer a response beyond ‘I don’t like it’, not to mention all the other socio-psychological effects that make respectful debate impossible and seem to have poisoned the epistemic commons. 

  2. Ethereum does this itself by not permitting contracts to autonomously call other contracts; mechanisms like MakerDAO’s liquidation process need to be triggered by off-chain actors incentivised through rewards. There are ways of solving the obvious trust problems, usually through incentives, if you believe they are problems. Much of the time they are not. 

  3. We know this, since there actually are a bunch of competitors

9:36pm. May 24, 2022.

Notes on Solidity Fridays Episode with Transmissions11

December’s Solidity Fridays with transmissions11 is really excellent, and I think a very high-leverage way to learn how good Solidity actually gets written. When the guest is especially good, as with transmissions, I put Solidity Fridays in the same sort of category as Destroy All Software – teaching by communicating models and patterns of thought, rather than regurgitating tutorial content.

Anyway, here are my notes in case somebody else finds them useful:

  • When emitting events, t11s emits instances of the contract rather than the address directly; the compiler will swap it out for an address anyway, but this approach gives you greater type safety

  • Call CREATE2 to revert if the contract has already been deployed, by adding a salt of the underlying address to the deployment

  • Addresses are 20 bytes, salts have to be 32 bytes. So we call fillLast12Bytes to add the remaining bytes (provided in Solmate’s bytes32 library)

  • The gas cost of > is equivalent to != when the comparator is 0, more expensive otherwise.

  • Illustration of his opinionated approach to smart contract development: “the performance of the code for users who aren’t stupid matters” - that’s why the [[ERC20]] implementation in [[Solmate]] doesn’t stop you from transferring into the contract’s address. “I’m not raising the cost for everyone else”

  • fdiv is like division, but accounting for the bases. ‘Scale this down by the contract’s base’. Multiply the numerator by baseUnit and then divide the numerator * baseUnit by the denominator. Also checks for overflow on numerator * baseUnit, since overflow isn’t protected in assembly calls.

  • In general, keep external calls all the way at the end of the function – including after any events are emitted – to make reentrancy more difficult.

  • More important with eg ERC777 since eg safeTransferFrom might allow arbitrary code execution (https://eips.ethereum.org/EIPS/eip-777)

  • Removing things from the end of an array is significantly cheaper than from the beginning, since in the latter case you have to move everything over.

  • uint256 currentIndex = withdrawalQueue.length - 1; for (; ; currentIndex--) is better in this case than initialising the uint56 i = withdrawalQueue.length - 1 since we’re only doing effects (the tx will revert by underflow automatically), so we don’t need to check the length. Saves gas.

  • Add a trusted boolean to the strategies, which is then checked on deposit and withdrawal. Makes it easier for EOAs to manage vaults without having to be wrapped in some other contract. Also makes it possible to disable withdrawal from strategies easily if they’re malicious in some way.

  • Two reads to the same struct from getStrategyData[strategy] has no extra gas cost, since it gets optimised by the compiler into one single SLOAD. & makes it clearer to read by a dev where it’s coming from.

  • Use unchecked when you know you won’t underflow or overflow, and so can therefore do without the safety. Saves gas.

  • unchecked isn’t leaky - it won’t uncheck in nested function calls

  • The implementation of Compound’s cToken is a little funky, since in a lot of places function calls return an error code rather than revert. So sometimes you need to require(cToken.blah() == 0) to ensure it succeeded.

6:38pm. April 6, 2022.

Links, March 2022

A lot of spacey content this month, and a lot of crypto, as I left my old job at Pactio and moved into crypto full-time:

Finally got round to reading Values by Mark Carney. Seesaws from economic theory to memoir in a not-uninteresting way. Carney writes well, but sets things up in such a manner as to make his premises seem more interesting than this conclusions. A very safe book. I imagine he’s going to run for public office in Canada some time soon.

Also enjoyed The Power Law by Sebastian Mallaby. Clean writing, thoroughly researched.

Curricula for self-teaching maths and physics from Susan Rigetti.

I wrote a couple of posts on being an enthusiastic amateur.

The first test image from the James Webb Telescope, of the star HD 84406 is pretty spectacular (and even more so the more you learn about it.) You can clearly see the spiralling of the galaxies in the background, each one comprising on average 100 billion stars, and many of them billions of lightyears away. The scale of space is very hard to comprehend.

Nadia Eghbal is writing again, which is always a joyous event, this first new essay a gesture toward a broader project on philanthropy and the tech industry. Her prose is both incisive and imagistic, twisting and deforming ideas in the best way possible, finding their veins, snapping them like kindling.

An essay on infinite ethics, an approach to ethics that takes the existence of infinites seriously, and how infinity fits into the logical structure of existing mainstream ethical theories.

All of physics in nine lines. I’m surprised the basic theoretical scheme of physics is so parsimonious. (Although it might not actually be that parsimonious and this is expository slight-of-hand. What, for instance, explains why there are 27 constants?)

A fun collection of weird ERC-20 contracts, mostly exploits or incompatibilities with conventions.

I enjoyed watching this episode of Solidity Fridays with transmissions11. He articulates trade-offs very well. I took voluminous notes that I’ll type up soon.

On top of my normal reading, I listened to three audiobooks this month. The first, Spacefarers by Christopher Wanjek, is freely available to Audible subscribers, and a smart and deeply technical look about the next thousand years of spaceflight.

The second, The Planets by Andrew Cohen and Brian Cox, is a book about the history and physics of the Solar System, a companion to the 2019 BBC television series (which is itself really excellent.) Samuel West’s narration is extremely good, and Cohen is a talented science writer.

The third, also available for free on Audible, was a collection of Scientific American articles about Exoplanets. The article format is helpful, and the narrator’s voice is just monotonal enough to fall asleep to.

Emily St. John Mandel wrote a series of notes on GoodReads, discussing various passages from her excellent novel Station Eleven.

Vitalik on the roads not taken.

The user experience problems of quadratic voting. It’s easy to evaluate an approach to some problem in terms of its technical feasibility, or how attractive it is with respect to various theoretical constraints. A lot of the time, its success hinges simply on whether people can understand it.

A very, very good blog post on NHS performance. We need more LessWrong-style analyses of British government policy.

5:46pm. April 4, 2022.

HD wallets and network switching

Blockchain ‘wallets’ are generally just pairs of public and private keys with some UI wrapped around them.1 We take the private key, and use it to derive the public key, which we then use to derive the wallet’s address.

What’s important is that the process of derivation is very difficult to reverse, in the same way that a hashing function is difficult to reverse: the chance of you guessing the private key correctly at random is about the same as selecting one atom from all the atoms in the universe – and there’s no better way than guessing at random.2 We can therefore use the wallet address publicly, being able to prove mathematically that we own it, without ever leaking information about the private key we used to generate it.

This works great, until you need more than one wallet. You might be concerned about privacy, or you might want to keep certain types of transactions separated for tax or other organisational reasons. If you have more than one wallet, you need to manage more than one set of private keys, back each key up separately, store each key separately, restore each key separately, etc. This presents a user experience problem: it is inconvenient, and clunky, and pushes a lot of the infosec responsibility onto the user. That might be acceptable for a bunch of nerds or anarcho-libertarians, but isn’t going to cut it for the median user.

The agreed-upon solution to these UX problems is Hierarchical Deterministic (HD) wallets, proposed in the Bitcoin BIP-32/44 standards and used by most other chains. This post considers this standard, how we’re not meeting it, and why it matters.

The plan, in three sections:

  • A short overview of what HD wallets are. Feel free to skip over this if you’re familiar with the spec already.
  • A discussion of how common wallets are not meeting this standard
  • A discussion of why that matters, and what we could do about it.

HD Wallets

Hierarchical Deterministic (HD) wallets take the basic derivation mechanism and encode structure into it. We take a master password – a single thing for the user to remember, to back up, etc. – and combine it with a path, a string following an a priori agreed-upon schema that allows us to generate multiple private keys from the same master password.

But it needn’t actually have much structure at all. You could simply take a master password and append 1, 2, 3, and so on, to generate different wallet addresses. This strategy would generate perfectly usable wallets with no obvious link between them. And since the generation process follows the same general sort of process as it does for the single-key case, the generation process produces hashed values that are similarly difficult to reverse.

We therefore only really need two pieces of information to calculate our wallet address:

  • Our master password
  • Some sort of seed

The master password is the user’s responsibility; it’s her input, her secret. What seed should we use?

One option is to let the user specify whatever sort of seed she wishes. But this doesn’t really solve our problem: instead of multiple private keys, we instead have to deal with a single password plus multiple paths. We’ve just given ourselves more passwords to remember.

Another is to do what I suggested above: append an incrementing integer to the end of it to generate different wallets. This is equivalent to giving ourselves more passwords, but at least there’s some rationale to it: our first wallet has a 1 at the end, our second wallet a 2, etc. It gives us some psychological safety: it means that our wallet is recoverable (assuming we can remember which number we used to generate it, or assuming we don’t mind iterating through a few guesses). This approach is fine, as far as it goes, but this is crypto, so, given the opportunity, we should make it more complicated.

A third approach is to develop a common standard for generating our seeds with more variables than just an incrementing number. This way, we can describe a tree structure independent of its values, embedding multiple values with which we might want to generate differing wallets. The benefit to this approach is that we can encode information about the purpose of the wallet into the seed itself, and then recover it later using our knowledge of those purposes without having to remember many arbitrary numbers. The standard gives us the template, and the purposes give us the values of the variables; all we have to do is fill them in. The other benefit to using a common standard is that wallet software can implement the standards too, so you don’t need to generate the wallets off-site somewhere.

This standard is called BIP-44 (it was originally a Bitcoin standard), and it presents this exactly this sort of predictable tree structure that we’ve been discussing. The goal here is minimises user input and maximise the number of wallets that can be generated with a single master password.

The standard calls the seed a derivation path, since it’s a path in a tree that we append to a master password and use the resulting string to derive a public address. The standard gives derivation paths the following structure:

m/purpose'/coin'/account'/change/index

And here’s the trick: most of these values are knowable by the wallet software, based on what sort of wallet you’re using:

  • purpose is always 44'.3 They gave it a value to allow them to upgrade the standard if they wanted to.
  • coin varies depending on the crypto network. For instance, coin = 60' is Ethereum mainnet, and coin = 966' is Polygon.
  • account gives the wallet a degree of freedom to support multiple user accounts (c.f. to the /Users/username directory on your OS)
  • change will generally be 0; it refers to whether the wallet should be used externally, or whether it should be use internal to the wallet for Bitcoin-based transaction change reasons. I’ve read somewhere that Ethereans sometimes use it, though for what I’m not sure.

The only non-guessable input value is index, which gives the user a degree of freedom to generate multiple wallets for under the same tree. This parameter is why the user can generate many wallets for a single password: she can keep incrementing index to generate more! It’s also exactly the same as my much simpler idea discussed previously.

These parameters then get put into the structure, like so:

m/44'/60'/0'/0/2

The structure then gets combined with the master password (or, more precisely, with a key generated from the master password), and users (or wallets) can vary coin, account and index to generate various wallet addresses.

Existing UIs and a Subtle Incompatibility

This isn’t a huge, bombshell-dropped discovery, I’ll admit it, but I’ve noticed that most wallets with support for both HD wallets and network switching don’t actually implement the BIP-44 correctly, or, at least, there is a tension between the model used for network switching and the model used for wallet generation.

Generally, what happens is:

  • Users add a master password (or its equivalent in the form of a mnemonic phrase) from which the wallet derives a single keypair
  • As far as I can make out, the ‘default wallet’ generated through this mechanism still uses the HD standard, it just relies implicitly upon the m/44'/60'/0'/0/0 derivation path (i.e. “give me external index 0 at account 0 for the Ethereum chain”).
  • When the user switches between compatible chains – from Mainnet to Arbitrum, for instance – the wallet software uses the same wallet address and private key to sign new transactions. It just switches the RPC endpoint it uses to make the request.

If wallets were to follow the standard correctly, they would be varying the coin value when switching networks, generating different wallet addresses for use depending on the network being used. In other words, according to BIP-44 at least, there’s no such thing as a ‘cross-network address’ – and existing wallets ignore this subtle fact entirely.

I’ve been looking at how various different wallets handle this, and they all seem to do the same thing:

  • Metamask’s network switcher is entirely independent from the wallet list, allowing the user to switch networks on the current wallet, even if that wallet was generated through a derivation path
  • MyEtherWallet do the same thing, switching the network URL used for chain interactions and not (as far as I can see) adjusting the corresponding wallets.
  • Similarly, there is nothing in the WalletConnect spec preventing this behaviour, meaning that any HD-compatible wallet software using the protocol facilitates wallet-independent network switching

The problem is not so much that nobody’s trying to follow the spec. The problem is that the spec is ambiguous with respect to the UI in which it’s being implemented. The community therefore has implicitly converged on this non-standard behaviour because of the ostensible UI benefits. This has created an implicit standard incompatible with the original BIP-32/44 proposals.

It gets even more confusing when you notice that there is a third, Ethereum-specific standard, EIP-601, designed to modify the BIP-44 standard for Ethereum use cases. From a brief google, I can’t see any mentions of 601 that aren’t merely links to the spec itself. But this ambiguity – what should happen to the valid wallet list when the user switches networks? – isn’t resolved by EIP-601 either.

This ambiguity is born because the BIP-32/44 standards were built around the assumption that the different networks a user might switch between were mutually incompatible. It didn’t foresee the rise of EVM-compatible layer 2s, and a range of dapps built to run on several of them concurrently, and therefore the capacity for the user to switch between them easily, in-app.

Why this matters, and what to do

Of course, this doesn’t seem like a critical problem – there are bigger problems we could be tackling, for sure. Indeed, there’s even something comforting about going from Polygon to Ethereum Mainnet and taking your address with you. It’s certainly convenient. But this isn’t what the BIP-32/44 specs say, and I think there actually are good reasons to obey them more precisely:

  1. It makes it possible to upgrade the spec in the future. The standard can evolve safely, and those implementing it correctly are able to evolve without having to hack in workarounds for backward compatibility, and keep track of previous fringe behaviours.

  2. It makes interoperability with other wallets easier. Wallet onboarding and offboarding isn’t a light matter; the more activation energy required to move to one wallet from another, or from no wallet at all, the more intimidating crypto as a whole will become to the marginal user. Problems at the tail-end often get publicised more than problems at the mean.

  3. Not doing so undermines one of the main reasons to use HD wallets in the first place: HD wallets allow you to keep public references to different addresses separated, increasing privacy. A wallet address that comes with you cross-network just makes your transactions that much easier to track.

Fortunately, I don’t believe that the UI concessions made by existing wallet implementations need to be locked in. There are some steps that wallets could take today, such as triggering a confirmation model when changing networks, that would enable users to opt-out of the spec. Many users don’t knowingly use HD wallets at all; in these cases, the default behaviour could just clear the wallet list and regenerate using the standard specs on network change.

Or, alternatively, we could develop a new, more parsimonious standard to capture the semantics of cross-chain wallets, compatible with the current UI approach. One simple method would be to amend the current spec such that network = 0 means ‘no specific network’, allowing cross-chain wallets to be represented in the existing spec. If a network changes while a user is connected with a wallet known to be generated with network = 0, the wallet persists.

Either way, this is the exactly the sort of subtle incompatibility that could prove to be an increasing nuisance, compounded by the ongoing growth in usage of layer 2s. Our standards for network switching were designed at a time when the only networks we would switch between were testnets. Today, the UI implications of network switching are a lot more important. And, today, that is incompatible with one of the most useful standards we have for managing multiple wallets.

Multiple wallets, multiple networks, good UX. We don’t need to pick only two.

  1. The name wallet is therefore a misnomer, since the wallet itself doesn’t store anything; it’s much closer to a username and password for online banking, than the vault itself. 

  2. Ethereum private keys are 256 bits. Since a bit has two possible states, guessing a 256 bit sequence correctly at random has a chance of 1/2^256. There are ~10^78 atoms in the observable universe, which is ~2^260. If you know the Ethereum address of the wallet you’re trying to get into it’s slightly easier, since wallet addresses are only 160 bits long, but it’s still a very big number

  3. The apostrophe in the path tells the key generation algorithm to use the ‘hardened’ form of the derivation, which puts extra constraints on the derivation such that derived public keys can’t be proven to be derived from a given parent public key using only public information. The details here are a little tricky, and outside the scope of this post.