# Unlimited Size Contracts Using Solidity

- Channel: [Devcon](https://streameth.org/devcon)
- Date: 2023-10-09
- Duration: 31:26
- Topics: blockchain, smart contracts, distributed ledger, ethereum
- Watch: https://streameth.org/watch/yt-qzlUNSt5pnA
- YouTube: https://www.youtube.com/watch?v=qzlUNSt5pnA

## Description

Visit the https://archive.devcon.org/ to gain access to the entire library of Devcon talks with the ease of filtering, playlists, personalized suggestions, decentralized access on Swarm, IPFS and more.
https://archive.devcon.org/archive/watch/6/unlimited-size-contracts-using-solidity/

Ethereum currently have a limit of 24KB for a contract size which cause a major pain for many Solidity devs. I would present all available options to address this issue using Solidity language during the workshop and provide cons and pros of them.
* External libs
* Transparent and uups proxies
* EIP-2535
* Static precompiled router proxy 
* Dynamic router
I will also provide gas optimization tricks for above methods and a novel method to reduce a gas cost for unlimited size contracts.

Speaker(s): Igor Yalovoy
Track: Developer Infrastructure
Keywords: Solidigy,dev tools,smart contracts

Follow us: https://twitter.com/efdevcon, https://twitter.com/ethereum
Learn more about devcon: https://www.devcon.org/
Learn more about ethereum: https://ethereum.org/ 

Devcon is the Ethereum conference for developers, researchers, thinkers, and makers. 
Devcon 6 was held in Bogotá, Colombia on Oct 11 - 14, 2022.
Devcon is organized and presented by the Ethereum Foundation, with the support of our sponsors. To find out more, please visit https://ethereum.foundation/

## Transcript

foreign [Music] everyone my name is igorio boy I'm a strategic developer a CEO founder and previously I worked at open Zeppelin and co-founded a D5 startup called Baby on finance today I'm excited to present you a workshop titled unlimited size contracts before we jump in the action you might want to download a GitHub repo for this Workshop either use the QR code or just Google GitHub while we Dash IO workshop and you'll get it we also will need a Foundry for this Workshop if you have it great if you don't have it just install it using the link in this slide and then run two commands Foundry up and Forge build uh I know internet is pretty bad here and so for some reason you can't download it and install it don't worry uh the repository will stay up and you have access to it and I'll pause the slides tomorrow and I'll be doing some live demos so you can just watch and get the idea what's going on let's jump right into it all right who has ever deployed here and written a smart contract please raise your hand yes I needed the developers okay who has ever run into the issue of a contract size limit here please raise your hands wow it's way more problem than effort so yeah this picture it illustrates my personal pain and why I work in baby on finance we pretty quickly ran at the issue of a contract size limit and it's not easy issue to address and it causes us a lot of pain and one file developer I know in a series space code with limitation I quote bane of my existence so this is how big this problem is and we are going to talk about today so there is a few solutions for this well you can reduce byte code size using various techniques and essentially it's like alternative sport to guess golfing I called it byte code golfing I certainly do not recommend it because it's like a diminishing return Sport with each optimization you have like less and less space left for each new function and it even May compromise the security of your project so I would be wary of using this technique and I suggest to try to design the architecture from Day Zero in the way it supports your contracts to be unlimited size other Solutions are external libraries static pre-compiled router and a dynamic rotor we will cover them in this workshop and hopefully have some time for questions and discussions all right we're gonna use a simple contract as a baseline contract for this Workshop it's called a counter it has a few functions a set function which sets a invariable to a new value a get functions which reduce this variable and finally a const functions which just adjusts the constant these are good functions for a baseline contract because they essentially utilize one as load operation One S store operation and returning the constant value and these examples are quite um well spread in real smart contracts all right um let's jump into the code uh here we have um our counter contract again I'm not going to spend any time on this it's a pretty basic what is important about it it's rather small so if we look at it byte code size you can see it's under one kilobyte which is great and we have a lot of space left but let's say a product team says oh actually we need to implement this new core feature called quote function the core function is a function which returns just like big string which takes all the space and then if you look at this big modeler it like takes almost all the space in the contract with a few left and if we try to implement it in very new way using solution inheritance so we just okay for our counter the big modeler like done job done and then we discovered that no the counters over the size of the limit we can't deploy it with nothing can be done it can be optimized so how we can solve it the first solution is external libraries external libraries is a is a feature of a solidity so how it works is that in your contract in your methods you just call a library instead of implementing it in the contract in this case we have a two libraries one is called storage lib which contains our original counter methods such as set get const and finally we have a quote Loop which contains the only quote method which contains this big string and if you look at the implementation details the catch here is that you need to pass this parameter which is canonically called self and has a storage type and this like a special syntax in solidity allows you to modify storage in external contract and essentially external libraries stability is just abstraction layer over delegate code and that's basically it there is not much to this and if we go back to our presentation uh we can quickly sum up all the ups and downs this approach so the good part about it it comes with some security guarantees by solidity the main important one is the library can't call self-destruct so a library can't destroy your smart contract which is important because it's not the case if you use diligate code directly essentially proxies where if a proxy contract calls so distract your contract is done it's somewhat easy to add one more leap you can always introduce one molip and it's gas efficient to a degree but a bit later on this and the main down downside so this is it it's not truly infinite because if we Define to add one more function you need like external wrapper function which stack up quite quickly and eventually you run out of space in your surface contract and it doesn't matter how many ellipse you can write more will be just normal space and the final downside maybe for some projects external libraries can be used can support upgradable contracts so once you deploy your external libraries it's like set in stone and you can't change it okay the next approach is a static pre-compiled rotor this work was pioneered by Alejandro Santander from synthetics he also previously worked at open Zeppelin and he is known as if you're not on Twitter and if you look at like if you want to take a look at production ready like stable code I suggest you check out hard hat router package at the synthetics repo and what they are going to take a look at the simplified example so so the idea um for the static router is pretty simple uh it's a smart contract which has no fun no external functions and it only has a fallback and receive functions and what it does it goes a forward function inside both of them and the forward function consists of two parts the lookup part for the implementation address and the assembly part for delete Jade call I'm not going to spend time on this I'm just going to say this is like a canonic implementation of a proxy a jade call in assembly which is used by all the proxies and what it does it basically sends your function code to whatever the implementation contract is and then returns its results and the interesting part is here so uh in solidity each function has a signature and as you can see here we have a switch for a signature method and based on the signature of code function we pick up an implementation address which is later used for dedicate call and as you can see here we have a four functions registered for a counter modular which is a get set const and one function registered for a quote modular because it's like so big and based on a function signature we do pick up a right implementation address and then use it for delegate call and again if you go to our contract sizes why it works because these are three separate contracts we have a big modular which is within the limits then we have um counter modulator which is also wind limits and finally static router itself is pretty small because it just with one function though the more functions you have the more it's bite-sized grows because you have to add each function here as a new line though it grows way less than external libraries example and if we go back to slides we can see what are the benefits of using it it's extremely gas efficient why because it's all pre-compiled in byte code so you don't need to waste any expensive operations as load on the router itself your function call goes to the router and then immediately calls delegate calls so you must guess overhead comes from Julie Jade call which is about 2600 gas it's almost infinite it will take really a lot of functions before you run out of space in this router in terms of adding with a constant signatures and contract sizes it doesn't require external functions well the downsides um pretty expected because it's essentially a proxy you can't use a Constructors you have to use initialize methods and cross modular calls can be tricky and you have to use explicit storage slots so you don't um your different model is going to write to the same storage and then finally develop and maintain so your head can be tricky because you need to pre-compute always signatures and addresses up front and you don't want to do it by hand which is I recommend check out again have hot router they built a plugin which does that for you well this like a second approach how you can achieve infinite size and compare to libraries it's like better in the way which gives you like more control using like assembly and low level language features the next idea is a duration on a static router it's not necessarily better it is a different approach it depends on your project I call it Dynamic router if you're interested in a product level better tested implementation of this idea I highly recommend you to check out EAP 2535 by Nick March vcp is covers far more things than just unlimited contract size but one thing it certainly does achieve is unlimited contract size through a dynamic router I've written a simplified example of this Dynamic router so let's get to it so we can see how it works um it's quite similar to a static router in the same way it has a fallback receive functions which just forward every function Call to um to our forward function but the difference comes in this line if in a static user function we have a switch case which just goes or have this for VC which I have this address in the dynamic router we are using a smart contract storage mapping which is called modulus and using his as you can see here we pick up an implementation address using a message signature and then the rest of limitations function is the same we just provide with implementation to the legit call and essentially it works in the same way the essential difference between static growth and dynamic is the static router has all these signatures and all the implementations and races hardcoded in its byte code where Dynamic router use uh mapping on the storage of a smart contract to track them and that adds pluses and minuses to this approach so the pluses is once you deploy your Dynamic router you can add more modulus even it's already deployed or you can like fix bugs by updating it here have this function update modulus and it's also pretty uh straightforward as you can see we just iterate and add selectors to implementations the downside of this approach is you have to manage access rights you you can't allow anyone to update functions of your router that would be disasters a critical issue in security terms and there are different ways to use it you can use open Zeppelin access roles or some like other techniques but that's like out of the scope of this Workshop but you just have to keep this in mind and if we go back to presentation Dynamic router is the first router which is truly infinite because all the mappings between uh function signatures and implementations are inside the storage which is as we all know very big in solidity then you can literally add infinite amount of functions to this router so it's very good for a complex system which would require upgrades and have like long expected lifetime then another option another plus I just mentioned it can be updated anytime well I mean it can be a minus if you're getting hacked but generally it's a plus has no external functions so the main downside or with is it I guess expensive with function is essentially here is a regional storage which is extra s load which is about 2000 gas and the set part you have to pay it for every contract call and then another downside is you have to manage access rights who can update this router if you want to build a truly Unstoppable contract maybe you don't want to have any upgrades and them can be many solutions like governments and multi-seek but you eventually have to figure it out for yourself these are the three main approaches uh which you can use to tackle uh infinite size contracts and then what's like important what's the difference between infinite size contracts and normal contact using this approach it comes down to a gas overheads essentially here in this table we are campaigning our base contract with with three approaches in terms of gas and why I guess it's important uh like a short story when was like a bull run and gas prices were high we were seeing the the function calls for the users in the price ranges of 100 dollars twenty two hundred dollars and even 300 for some projects and a lot of these gas costs were coming from using proxies because proxies has a gas overhead at each function call and if you really have a complex defy system where protocols call each other and they all have there are all proxies with amount of overhead Stacks pretty heavily even within your own protocol and it's like important to keep an eye on this gas overhead course so let's dig down a bit in these numbers the first base contract are the numbers for our base course so it's like no delete code involved and then if you look at that the base call for a constant call it just returns a number it's a very small and the first number in red is just what's the overhead and guess and the second number in red is in percentage and you can see for like a simple return call of a constant value the overhead is actually around two thousand percent which is like insane and you may say oh it's not practical you know to return the constant value but you'll be far from the truth because the famous uc20 token has a member a function called decimals which returns how many they came also talking has and you have to call this function if you define developer because in order to handle ec20 token you have to know how many decimals this token have and one of the most common uh uc20 tokens like usdc their approxy contracts so every time you call decimals you overpain roughly two thousand percent or probably more but anyway let's take a high level look at this approaches so as you can see indeed static router is the most gas efficient approach so if you have to build a gas efficient infinite contract and you are and you know what kind of functions you're going to have and don't plan to upgrade then it's like a great solution and then Dynamic router is make more expensive even more expensive than external Library um the reason I wouldn't recommend external Library as you can see it has unusually high overhead for quote more than double than it should and I think it has something to do with how solidity pass with like big amounts of data but I haven't dipped the byte code yet but just be wary if using external libraries and you pass or return big amounts of data then your gas expenses may surprise you and to take a look at this uh what would be the high level uh conclusions of this overview is that uh your gas overhead obviously is more significant for uh low fun for actions which are low in gas essentially if your function has only a single s load then you would pay a lot of gas so what it means in Practical terms which I also encountered is that if you need to build a function you should learn some like data around your protocol smart contract you may want to park them all this data in one function call so you don't need to call five functions in a row because you would pay a gas overheard on each call essentially batching view functions might be a good idea then again the bigger the Baseline gas cost for your function the less you care about it so let's say you have a function which is quite expensive let's say 300K then suddenly five five key overhead doesn't look that bad um though there is a catch to this in many protocols not all functions are equal essentially if you look at like typical vote functions such as deposit or withdrawal which are mostly called by the user so you won't achieve uh the maximum gas efficiency on these functions and some like admin functions which are rarely called you may want you know to skip optimizing yes for them and um because the guess is so important I spent some time thinking and trying to figure out a trick how we can improve this and I came up with solution which we use successfully in production to reduce the gas cost of our contracts we never found anyone using it um somewhere on the Internet or on GitHub which is why um I had to coin the term for this approach so I'm going I'm calling it like loaded router and so the approach is pretty simple because like if you go back to a static router so static router in our case um it doesn't take much space and even it grows there's still a lot of space left and what you can do essentially is again using solution inheritance you can inherit some functions of your contract to a static router uh which allows them to be as guest efficient as the contract itself because it's essentially a contract itself so in the other ways every some space in terms of byte code size left in your static router you can put the functions there which either people use the most in your system or the functions which has the highest gas overhead like calling decimals and this allows you to save quite a bit of gas because um just to give better explanation of this uh if you combine static router with a counter modular we don't get to this forward function because static router now is counter modular and that results in a quite significant gas savings and the same approach can be applied to Dynamic router I'm just gonna show you the call but it's a pretty uh straightforward you again can hit counter model but here this is the cache with a dynamic router because in Dynamic router we can update implementations of a function if you place a function like get a set on the router itself you will never be able to update it so that is why it's like rather risky and you should be very mindful which functions you put on Dynamic router because you don't want to be in a situation where a critical bug is found in the function and you can't update it and it's just like a terrible situation to be in and with this approach we can crunch some guess numbers so now here we're using routers themselves as a Baseline and then the loaded version essentially as Improvement and as you would expect because we move a constant function from sitting behind the delegate call back on the router it results either to 95 Improvement or 97 Improvement for a constant function it results around 54 or 68 Improvement for a get function which is a single s load a singular slow function is not that uncommon as you might think because for example you see 20 balance of function is just a single s load and again this function is called a lot and I don't really expect it to be changed and then finally for a single s store operations uh the savings amount is about 11 or 18 so it's like not that much but like still um sizeable and what you could you like looking at these numbers my suggestion would be is to move a logous function on the router if they use a lot so this is the most best saving or just the functions you expect uh user Fade to be user facing so the in the functions people will use the most again as a transfer function the deposit of withdrawal or claim reward and then finally the pure functions you just adjust the constant Health the biggest impact on this uh and just gives you some practical meaning let's look take a look at the two most common uh contracts is essentially C20 token and erc721 which is an nft foreign we can see surprisingly many functions of pc20 function 20 token the station's name and symbol in decimals in most implementations they really return a constant you know just like name is like my great tokens involved with like three letters and they came out with like 18 number right and in most projects really with functions will never change and virtually if you write in UC 20 token which has decimal functions which will return different values I might have a problem with you please let me know and um even going back even on later so total Supply many AC 20 tokens has a fixed total Supply right so with again no reason for this function to be upgradable or to ECB sitting behind the daily rate call um and then things get tricky so I can see how transfer allowance or proof can be changed in the future maybe standards change but it's like up to every developer you have to make call on yourself whenever your project uh View encounter these issues in the future or not and um the same logic more or less applies to ec721 but these are just two common contracts I really want you to show on them kind of logic I would fall if I would do a complex system where I would just sit down and look at my contracts and look at which methods are uh not going to change for sure and visual X save to put on the router itself to save the gas and which makes the most sense because for example with baby on finance we had this like admin model which had like a lot of admin functions but it's pretty much only us or governance we're calling them so we really didn't care about the gas gas cost of it so I was a confident just like putting them behind the digit call and being upgradable and let's let's uh jump back to the code so here you would see once we put these functions on our routers they do grow in size so a loaded static router is slightly bigger than just a static router that's because we merged all these functions on this contract and eventually you hit the size of how many functions you can put on your router which we did and that is why you also should be like mindful that you have to put the functions on the router which like your absolute top priority in terms of gas so if it's like questionable don't do it and the same applies to Dynamic router so it's higher but to repeat uh secure my security you can see on concern once again um Dynamic router is different from static router in that sense that if you put a function on router itself then it can be upgraded later and you should be like super careful doing this the code uh the repo is already public and it has all these examples as well as the test which I just made which actually makes sure that all we discussed is worked so here you can see that creating static routers is simple because it has everything encoded in it while to create a dynamic router we need to build all these modules so we need to dynamically fetch all the selectors for the methods and then we create a model definition which is an array of selectors bind it to a certain implementation and finally we can use with modulus to initialize our Dynamic router with update modulus and this is the same way you can update existing functions or add new even after the deployment and here we have tests to make sure that all these functions work and if you will be checking out with Reaper later you can run the following command test guest report to see all the guest numbers we just looked and speaking of these guess numbers um these numbers are produced on 0.8 solidity compiler with optimization set on and about 200 optimizations run on let's get back to our presentation and that will be it thank you for your time I hope you find it useful I hope you build unlimited contracts successfully and never experience the pain I did please follow my Twitter I do Post development stuff there
