Building Scalable protocols with clones by Nico (@tenderize_me)
ETHDubai 2023 · 16 Mar 2023 · 8:39
Nico explains how Tenderize uses clones and proxies to build modular, scalable smart-contract systems.
Speakers
Topics
From ETHDubai 2023, hosted by DeFi Dubai.
Transcript
hi everyone I hope everyone had a great lunch and is having a great time at Dubai my name is Nico I'm the founder and CEO of China Royce and a presentation just one week uh it was just on the screen saver okay let's go so I'm going to give a little bit of a technical talk today actually prepared this because I thought there was going to be a hackathon and I was informed that hackathon was yesterday and before the event but nonetheless let's
still have the talk and we'll talk about building scalable protocols with clones like I said my name is Nico from tenderize founder and CEO before tenderize I worked for Life peer for three years building their smart contracts and protocol so this will not be a deep dive into proxies if you want to learn more about proxy contracts I suggest you go to the Defcon archive and check out Alice in proxy land from Alejandro it's a really great talk on on proxies and the credit for the ID that we're talking about today it's not my invention the credit for it goes to
zephron from pseudo Swap and wigawak from heart at deploy so why do we use proxies in smart contract developments two reasons upgradability and creating multiple instances of the same code if we drop upgrade ability if we just want multiple instances we basically get clones of the same contract and why do we need clones at tenderize why do we need multiple instances of the same contract we're a liquid staking protocol and what we do is basically build a erc20 token and Vault for every validator on the
network so it's the same piece of code deployed over and over again for example in the case of the graph Network there's like 300 indexers so that would mean we would have to deploy the vault in the token 300 times which is not really that cost efficient you can also think of something like uniswap whereby each liquidity pool is the same piece of code just with different tokens and parameters that it handles so let's see how this actually would work without a proxy first and we're just going to set an immutable variable a on a contract so this talk will have a bit of code in it I was told this was
not a good ID so I hope it's like readable so if we just have a regular contract my contract and we want to set an immutable variable we just do it in the Constructor when we deploy the contract and the variable a will then be filled in in the runtime byte code of the contract now if we want to switch to a proxy this becomes really problematic you can't use a constant or an immutable with the proxy so variable a will be saved in storage instead and because we have to because we're using a proxy we also have to
match the storage between the bottom contract and the top contract otherwise you can get storage collisions which could lead to very bad things happening to your contract so here the top one is the implementation and the bottom one is the contract and users would call the bottom one to execute the logic of the top one but the storage would be written to the one below but if we now want to access the parameter a and this can be for example the token that we use in a liquidity pool or for our case the validator that a specific fault will stick to then this becomes a very expensive storage read each time we want to access this
so as to summarize with a prox you have no immutables you use storage operations which are very expensive and you need to make sure that the storage layout of your proxy contract matches the implementation and that has led to many exploits already over the course of the last few years not matching the storage layout correctly so how does this happen if we use clones with what we call immutables you basically have a contract and it has a function to get the value of a but you see there is no declaration for an immutable variable variable there is no declaration for a
storage slot for a yet if we call get a we will be returned the value that it's supposed to be and if you see in the middle right about it actually loads something from the call data how does this work so when we deploy a clone we will actually deploy it with some arguments attached to it here you can see in the first green part that we create our arguments in this case we want to set the value of a to 1 3 3 7.
and we then deploy the Clone with those arguments and if we then call get a on our contract we will get the value returned but like we said it was never initially declared here in the contract so what kind of sorcery is this and how does this actually work so let me explain a little bit of the magic to you this is on the left you see basically the library of clones that was written by zafram and wigawag and the essence of what it does is the first part of the bytes memory immutables the arguments that we Supply they are actually appended to the byte
code of the proxy contract you're creating or the Clone you are creating and then with every call you make to your implementation what you have added to the bytecode at the end is appended to the call data of the function you call so you can access this this piece of call data with the arguments from your implementation contract and what it achieves is basically you get the same characteristics of using immutables or constants but then with proxy contracts the only downside is that the amount of arguments you can append to the byte code is limited in length and it would the entire length of the
appended bytecode would be added to the call later but luckily call data is pretty cheap so it's not really that big of a problem and as a practical example how do we use it in tenderize we have four different parameters that we want to get cheaply and that is the asset that is being staked the validator validator being staked to and then two internal modules that we use and none of these functions actually use storage slots so it makes it really cost efficient for each instance of the contract to fetch the parameters it needs and it solves a big problem of otherwise having to use initialize functions in your proxy contracts or
your clones which is not really nice for ux and if you don't do that correctly can also lead to severe exploits so I'm keeping it short today so thank you for listening to this technical talk s for saffron and Wiggy work for coming up with this if you want to know more about tenderize you can visit our website or we have a booth all the way in the back to the right feel free to buy we still have a few t-shirts and shorts left not many but yeah you're welcome to pick them up thank you everyone