You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Farcaster must be a long-lived protocol that is capable of surviving for many decades like email, http or tcp. Such systems must be upgradeable by design or must be so simple that they are unlikely to be changed. The Hubs are an example of the former, while the IDRegistry is an example of the latter.
The NameRegistry, which lets users register fnames, finds itself in a tricky situation. It has a fair bit of complexity since it issues renewable ERC-721 tokens, has an inbuilt auction system and an admin system to manage and reclaim names. However, unlike the Hubs, it is a smart contract which is much more difficult to upgrade.
Given the constraints of a trustworthy and decentralized NameRegistry, it seems impossible to build a long lived registry that doesn't have some form of upgradeability. During its lifespan we may need to address problems like rapidly increasing gas fees (which may require moving to an L2 or rewriting logic), redesigning economic incentives that are being gamed (by changing the fee system or auction system) or introducing new features to solve for problems that are unknown unknowns.
Solutions
Contracts that face this problem today either take the approach of becoming upgradeable or relying on a manual migration at some point.
A migration involves deploying a new contract and moving all existing state over some period of time. The challenge is that the Name Registry's state increases linearly with each new user which might make this economically infeasible eventually. Upgrades cannot be done slowly over time like with Uniswap, since a name registrar needs to be aware of global state. For example, it cannot let you register @alice if even a single person has minted that name before which requires having all state available.
An upgradeable contract uses a pattern like UUPS or Diamonds which allows the contract logic to be changed at any point. Such patterns come with tradeoffs including higher gas costs, more foot-guns and a much larger surface area for attacks. They also have some limitations when it comes to changing the storage location to another contract or network.
Changing the path you start on isn't irreversible because you can always perform a migration out of each state into the other. But this is neither easy nor cheap and should be avoided.
Option A: UUPS Proxy
We stay the course with our upgradeable system, choosing not to move to a new architecture.
No extra work needed, likely the fastest path to testnet and mainnet
Cons:
Gas costs increase by about 400 per call because of proxy, and a little more because we can't use Solmate
Easy to foot-gun storage leading to unexpected behavior
Larger surface area for attacks and bugs when compared to C
Very little room for adding features (only 2KB left to grow)
Option B: Diamond Proxy
We rewrite the contract to use a Diamond Proxy either now (if straightforward) or on testnet, performing a one-time migration from UUPS to Diamond contracts, before performing another upgrade on mainnet. This will be fairly expensive but is a bet on the future since Diamonds are becoming the common standard.
Pros:
Contract has a very large size limit, allowing unbounded growth
Likely to be the most common way to build contracts in the future, even though bleeding edge today
Cons:
Gas costs increase by ___ because of contract-to-contract calls
Gas costs increase by (100?) because we can't use Solmate contracts
Learning curve is much steeper than A
Larger surface area for attacks and bugs when compared to C
More unknown unknowns when compared to A
The slowest path to mainnet, since it requires a significant rewrite
Option C: Migration
We remove any notion of upgradeability from the contract. If changes are needed a migration will be performed which can happen one of two ways:
Protocol-Funded Upgrades
A new v2 contract is launched in an "admin-only" mode while v1 is paused. The protocol makes a few transactions to re-register users on the new contract paying for all the costs. We can perform protocol funded upgrades a few times while the userbase is small (<100k)
User-Funded Upgrades
A new v2 contract is run side-by-side for a year while v1 is paused. The v2 contract has a merkle root of all v1 names which cannot be registered. Users with v1 names must "upgrade" their name by burning it into the v2 contract at which point a new one is issued paying for the gas to transfer their names . Names still on v1 will not be recoverable until they are upgraded to v2. There will be some churn as users elect not to pay for the gas cost and renew their names on the new contract. We can only perform upgrades if they are user funded pay for it beyond a certain size (> 1M)
Pros:
Lower gas costs than other option (500+ savings per call)
"More decentralized" since there is no way to change functionality
Cons:
History is split across contracts, creating more work for developers to rebuild state
Requires explicit changes made by each developer consuming data from the contract.
Cost of migration can become very high and must be borne by network or users
Some % will always fail, hurting the growth of the network by causing churn
Requires very thorough auditing and testing to ensure no bugs before mainnet launch (might add 1 month of contract work)
Option C is a very robust choice if we believe a few things:
We can design a contract that, with a small number of planned migrations early in its lifecycle can be come robust and last ~ 10 years without any more migrations.
In the next 10-15 years, scaling solutions will arrive that reduce the cost of migrations by 10x from where they are today allowing us to perform migrations again.
We do not believe gas-to-dollar prices will reach and exceed previous all-time-highs for an extended duration (say > 3 months)
Appendix
Migration Cost Estimate
A "floor" cost for the current migration can be arrived at by reasoning about the minimum amount of data that needs to be replicated to the new contract. If we assume that the only data that needs to be replicated is the expiry timestamp, recovery address, owner state and balance state that leaves us with 4 cold SSTORE's which is 80k of gas.
/// @notice Maps each the uint256 representation of an fname to the time at which it expiresmapping(uint256=>uint256) public expiryOf;
/// @notice Maps each uint256 representation of an fname to the address that can recover itmapping(uint256=>address) public recoveryOf;
/// @ ERC-721: Mapping from token ID to owner addressmapping(uint256=>address) private _owners;
/// @ ERC-721: Mapping owner address to token countmapping(address=>uint256) private _balances;
Based on the last year's worth of price data the per user migration cost might be:
The gas price can range from 3 gwei to 300 gwei
The ETH price can range from 0.00024 ETH to 0.024 ETH
The dollar price can range from ~ $0.25 to $100
These numbers also don't account for the cost of the "logic" and the fact that ETH prices can go higher than they have before. An 80% confidence interval for upgrading a million users in the next few years is $250k and $10M, though there is no theoretical upper bound on how high it could go.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
-
Problem
Farcaster must be a long-lived protocol that is capable of surviving for many decades like email, http or tcp. Such systems must be upgradeable by design or must be so simple that they are unlikely to be changed. The Hubs are an example of the former, while the IDRegistry is an example of the latter.
The NameRegistry, which lets users register fnames, finds itself in a tricky situation. It has a fair bit of complexity since it issues renewable ERC-721 tokens, has an inbuilt auction system and an admin system to manage and reclaim names. However, unlike the Hubs, it is a smart contract which is much more difficult to upgrade.
Given the constraints of a trustworthy and decentralized NameRegistry, it seems impossible to build a long lived registry that doesn't have some form of upgradeability. During its lifespan we may need to address problems like rapidly increasing gas fees (which may require moving to an L2 or rewriting logic), redesigning economic incentives that are being gamed (by changing the fee system or auction system) or introducing new features to solve for problems that are unknown unknowns.
Solutions
Contracts that face this problem today either take the approach of becoming upgradeable or relying on a manual migration at some point.
A migration involves deploying a new contract and moving all existing state over some period of time. The challenge is that the Name Registry's state increases linearly with each new user which might make this economically infeasible eventually. Upgrades cannot be done slowly over time like with Uniswap, since a name registrar needs to be aware of global state. For example, it cannot let you register
@alice
if even a single person has minted that name before which requires having all state available.An upgradeable contract uses a pattern like UUPS or Diamonds which allows the contract logic to be changed at any point. Such patterns come with tradeoffs including higher gas costs, more foot-guns and a much larger surface area for attacks. They also have some limitations when it comes to changing the storage location to another contract or network.
Changing the path you start on isn't irreversible because you can always perform a migration out of each state into the other. But this is neither easy nor cheap and should be avoided.
Option A: UUPS Proxy
We stay the course with our upgradeable system, choosing not to move to a new architecture.
Pros:
Cons:
Option B: Diamond Proxy
We rewrite the contract to use a Diamond Proxy either now (if straightforward) or on testnet, performing a one-time migration from UUPS to Diamond contracts, before performing another upgrade on mainnet. This will be fairly expensive but is a bet on the future since Diamonds are becoming the common standard.
Pros:
Cons:
Option C: Migration
We remove any notion of upgradeability from the contract. If changes are needed a migration will be performed which can happen one of two ways:
Protocol-Funded Upgrades
A new v2 contract is launched in an "admin-only" mode while v1 is paused. The protocol makes a few transactions to re-register users on the new contract paying for all the costs. We can perform protocol funded upgrades a few times while the userbase is small (<100k)
User-Funded Upgrades
A new v2 contract is run side-by-side for a year while v1 is paused. The v2 contract has a merkle root of all v1 names which cannot be registered. Users with v1 names must "upgrade" their name by burning it into the v2 contract at which point a new one is issued paying for the gas to transfer their names . Names still on v1 will not be recoverable until they are upgraded to v2. There will be some churn as users elect not to pay for the gas cost and renew their names on the new contract. We can only perform upgrades if they are user funded pay for it beyond a certain size (> 1M)
Pros:
Cons:
Option C is a very robust choice if we believe a few things:
We can design a contract that, with a small number of planned migrations early in its lifecycle can be come robust and last ~ 10 years without any more migrations.
In the next 10-15 years, scaling solutions will arrive that reduce the cost of migrations by 10x from where they are today allowing us to perform migrations again.
We do not believe gas-to-dollar prices will reach and exceed previous all-time-highs for an extended duration (say > 3 months)
Appendix
Migration Cost Estimate
A "floor" cost for the current migration can be arrived at by reasoning about the minimum amount of data that needs to be replicated to the new contract. If we assume that the only data that needs to be replicated is the expiry timestamp, recovery address, owner state and balance state that leaves us with 4 cold SSTORE's which is 80k of gas.
Based on the last year's worth of price data the per user migration cost might be:
These numbers also don't account for the cost of the "logic" and the fact that ETH prices can go higher than they have before. An 80% confidence interval for upgrading a million users in the next few years is $250k and $10M, though there is no theoretical upper bound on how high it could go.
References
Beta Was this translation helpful? Give feedback.
All reactions