New Ethereum talks, every Monday. The week's conference uploads by event, in your inbox.

Loading player…

Safe and robust smart contracts in ink! with Piotr Moczurad

Ethereum DenverMon, Oct 9, 2023, 12:00 AM

"Crash course on coding smart contracts in Rust/ink! and what WASM brings to the table.

Transcript

hi everyone I'm Peter maturat you may also know me as Peter rinsky from Discord and Twitter and such and uh yeah it's my pleasure to present uh a short tutorial on Smart contracts in Inc uh specifically on the outlet zero chain but uh actually I think you'll find a lot of the the principles will apply to well smart contracts generally some of them and all of things smart contracts definitely so let's Dive Right In um yeah so I I felt you know compelled to to put the definition of a smart contract here it's it's not really important but I think it's worth noting that basically a smart contract is is a program that executes on on the blockchain actually this analogy uh Works quite well along the way as we go um so the smart contracts give way to some interesting use cases uh I think the most prominent one being defy so decentralized Finance but also digital identity with the whole kyc KYB use cases and uh yeah you may do you know supply chain and provenance tracking in in some big company also a pretty exciting use case is certificates of ownership be that art real estate licensing or tickets but of course uh you know you can you're actually encouraged to come up with your own use case and uh and implement it so let's hope you know through this presentation you'll be interested enough to to dig deeper and and to deploy your own smart contract and and uh and an application on the blockchain um so maybe a slide forward this this presentation is is supposed to be quite General so I wouldn't expect to you know learn all the ins and outs of of blockchain development or or Inc even but rather you know after this I would like you to have a basic overview of of you know how to approach blockchain development uh well smart contracts specifically and um and yeah I will encourage you to you know exploring your own and um and yeah just just start developing um so for now we're gonna start with some well let's say theoretical introduction um because we're gonna look at decentralized finance and other another definition that's well from yqtds so uh yeah you know that's a that's a very credible Source um so basically decentralized Finance you can think of it well you want to interact with some Financial financial instruments without relying on on some trusted intermediary uh you know Exchange Bank or whatever you want it to be censorship free and and everything so you do that on the blockchain in form of smart contracts and here as well we have some very interesting examples including decentralized exchanges like uni swap or common that will be coming to our zero quite soon you have Landing protocols uh trading crowdfunding platforms like Angel block for example you have payment protocols and and many many more actually um that's you know I'm leaving you leaving this up to you to explore and uh we're gonna look at one example because I find it quite quite interesting and actually quite uh quite educational and also quite beautiful in its simplicity so you know how there are well centralized exchanges that keep the order books and you basically you can think of it as a as a building and uh there's a there's some computer running in the building but actually there there could just be a like a physical book where you keep the the the the limit orders and everything and well the problem here is that it's completely permissioned and someone could just restrict your access or say okay now I'm taking you know uh custody of your funds so you know I would like to compare that with the with the new way uh specifically a decentralized exchange and you know it's it's cool because the decentralized exchange is running as a smart contract on the blockchain so there's there's no single Authority right the contract is just a program that's executing and it's actually completely transparent because you can you can inspect this code and see what it does so you you will never be surprised and um we're gonna look at the automated Market maker concept here uh but let's just note that this is not the only way to do a decentralized exchange you can also actually do an order book uh something that's very interesting but the amm approach is well it's kind of beautiful in its simplicity so for the sake of this presentation and to keep it you know uh light and interesting uh I'm gonna just you know mention a few words about how this works so basically the exchange keeps a liquidity pool or actually like maybe liquidity pools so if I want to provide the liquidity to The Exchange I just take some well let's say I take some some of the tokens uh actually a token pair and I deposit a hundred of of token one and let's say 200 of token two so now the exchange has a pair and uh and you can kind of see that the ratio is two to one for now but in general what the exchange will try to do is to and this is this is slightly simplified but I think it it actually illustrates this quite well so the exchange will for for a single pair of of Tokens The Exchange will try to keep their their product as a some constant value well let's call it k um so now you can you can see that if I do a trade and uh and actually want to trade sum of token one for token two it's very easy to calculate what's the what's the amount of token two that the exchange has to has to give give back to me right uh we actually have it here uh Illustrated a little bit better so what we will do let's say I want to exchange 10 of token 1 into a certain amount of token two so I just give the 10 to the exchange it adds it to the amount of token token one then it increases so in order to keep the K constant the the amount of token 2 must decrease and the this amount and of course you can calculate it quite easily and uh and the decrease in token 2 is basically what I get back uh as as the as the exchanged exchange token now notice that the the K here is constant for a certain liquidity pool when I provide the liquidity to The Exchange the K basically will need to get recalculated um so yeah so I just wanted to to show you how you know a very simple idea and actually the resulting smart contract is also really simple and uh it's very expressive and you know it can work as a fully functional product this is kind of like uni swap V2 concept and uh also actually a neat property of this is that the exchange never runs out of liquidity because if you if you even take a look at this curve here you will see that it just approaches approaches zero on both sides but never quite gets there so this is this is a cool property um right but you know we are seeing how how expressive you can get with smart contracts and how how actually powerful the the whole concept is but uh well it's actually handling you know vital assets and the user deposit their funds and everything so of course well we need to make sure that the the contracts are are written properly and uh and there's well there's no possibility of any exploits or attacks or malicious behavior and uh yeah here here it's uh stated kind of like kind of like a joke but uh the grand theory of hacks if you if you may but my my stance on this is that basically the exploits happen in the places of your code when you're not 100 sure what the code does right and it sounds very trivial but but this is actually the case right if you if you have a language that's not safe enough by by Design or you know it's especially easy to shoot yourself in your in the foot the the exploits of course uh will happen much more often and um and yeah this is this is kind of you know this kind of will lead me on to to to talk about ink which I think it's it's designed really well and you know doesn't offer you as many uh ways of shooting yourself in the foot and it's quite safe but uh in general and this applies to I guess all all code but but smart contracts and smart contracts anything specifically uh you'll need to watch out for you know input validation basically if the input is not validated then your contract can get something it does not expect well there you have it I.E attack Vector uh actually and this is well basically my background as a functional programmer talking but any operations that mutate the state of the contract are inherently dangerous right so of course you you cannot avoid them and they're essential to the functional functioning of your program but uh it's actually nice to to kind of make them kind of well separate or or treat them with special care in in your contract um stuff like the code repeating itself so not only Loops but especially recursion of any sort and contracts calling themselves a you know really important area to focus on and a lot of attacks happen happen there actually um especially when you add like catch all methods like uh like solidities fall back uh and any implicit operations this is something to really really watch out for so so yeah so to kind of demonstrate this idea I wanted to talk about the famous tower hack uh which happened of course a long time ago and the the problems have since been fixed but the it actually illustrates quite well what I what I wanted to convey here um so the way this happened is that there was a dial contract which for you know to to to focus our attention it basically allowed you to to deposit and withdraw some some eth right um so and now it's important to to know to note that the way solidity works it you can have a fallback method in in your contract which basically says okay if the contract is asked to do anything that it's wasn't programmed to do the fallback is like a casual method that that will that will handle this right additionally um and it's the same in solidity and an ink but handled a little bit differently uh you can send like the change the native currency to the contract so in this case EDH and uh in solidity in order to to handle the the transfer of of actual eth you need to have the receive method but if there's no receive method the the contract falls back onto the onto the fallback method so now for this we'll we would need to take a look at the at the code of the of the Dow contract but the the gist of it is that the contract was when asked to withdraw some eth the contract was basically transferring the the the eth to the color and only then decreasing the balance of of the color in this contract so what someone did and uh you know the classic uh names used in Crystal in cryptography would be Alice Bob but here it's well I thought it would be funny to call call the attacker malice just because it sounds like something very evil um so what malice does is well uh she first deposits 100 eth of course can be any amount actually and then asks the dial contract to withdraw the D100 ADH and um of course the contract checks if if she actually has this balance yes sure and then the contracts issue the contract issues a transfer to malice now malice doesn't have the receive function in her contract so it falls back to the withdraw to the it falls back to the well fallback method where they actually put another withdrawal and remember that now it gets all gets executed in the context of like a single uh well single execution step so the dial contract hasn't yet progressed to the step where it decreases the balance right so basically what happens is the the contract enters the the withdraw method again the dial contract and the balance hasn't yet been decreased so it says okay I have the funds to transfer to malice and basically this Loops this Loop happens as long as the dial contract has any any funds so this is how you were able to drain the whole contract and actually you can see that the fix is very simple you can just flip the order of of the lines and first check at first decrease the balance and then actually make the transfer but well we we live and we learn right so this is just you know an educational example to serve as a warning to well first of all avoid any implicit and catch-all methods like this and secondly you know this underlines the importance of actually writing a code in a very uh diligent and and you know cautious fashion yeah so let's move on um so let's let's talk about you know where we are gonna deploy our smart contracts and uh what platform are we going to use and well for the sake of this discussion we're gonna use Alex zero which you know is a layer one proof of stake blockchain with some very neat privacy features that we are building out right now sub second finality times and also block times actually uh extremely low transaction cost I cannot really read this number very well but it's you can see that it's like a fraction small fraction of a zero which actually is quite crucial to Smart contract development right it influences how you how you approach the development and maybe you won't have to spend you know like hours uh tweaking the byte code because you just you know you can just write out your logic and it will and it will be priced reasonably and of course uh it gets smart contract in ink introduced well not that long ago but uh but they are there on the mainnet now and uh a lot of people who are building out their use cases it's it's a quite exciting time to to be on the Chain so let's you know let's take a look at uh how can we Implement a smart contract in Inc uh but first here's some useful links for you you can scan the QR code uh I think the slides will be attached so you'll be able to click on the links the first one is the main Alex zero website and actually you'll be able to to get to all the other links uh via this website in like one or two hops but then you have the other zero documentation uh the ala0 developer portal which contains links and like resources to to all the ink tutorials that you that you will need and then the developer wallet test.a0.dev uh of course we support other wallets but this is this is like the well let's say feature complete one for for developers specifically and uh you know for validators and then last but not least the the contracts UI um which allows you to to upload and interact with your contracts obviously in in quite a user-friendly fashion uh we're gonna actually touch upon that later later on in this presentation so yeah so first of all what is Inc right the language that we used to to write the smart contracts it's an edsl for rust so an embedded domain specific language uh built on top of rasp so it's not a separate language uh you don't have to learn something entirely new and and the whole ecosystem um basically you can you without loss of generality we can say that this is a subset of rust with some additional macros so uh not all edsls are created equal and ink is on the like more minimalist side let's say so it doesn't add any any fancy constructs it's just a subset of rust and some some macros that you need to include uh but you know on account of being essentially a such sort of rust it inherits rust's benefits so so being modern um like really this is a well-researched language uh with with a lot of modern features you won't get to use all of them in when writing smart contracts but uh but yeah it's still good to keep that in mind uh it's it's type safe I would say S-Type safe as a practical programming language can be uh it's memory safe and actually I encourage you to to dig a Little Deeper on rust's memory management we're going to touch upon that very briefly but but it's it's a very interesting read and again when writing smart contracts you won't have to you know dive very deep into how wrath manages the memory a lifetimes borrowing and everything but still I think it's very it's a very interesting concept and and one of the best implementations I've seen personally the the tooling is is quite good obviously it's in terms of smart contracts uh solidity is is more mature and has a way bigger tooling but but Inc well and rust is catching up really quickly and uh I think you know there's a lot to like and uh of course rust is universally liked in the stack Overflow survey rust is coming out on top as the most liked by developers uh language programming language uh I think it's it has been for a few years now so now the the let's say flavor of rust or the back end that we're gonna use for rust uh specifically for smart contract development webassembling and I I don't think I need to you know dive deep into the benefits here but uh it's ubiquitous so you know the browser supported some different execution environments supported that chains supported of course it's it's fast and secure and um yeah since we were talking about wasm I have a it's like a personal you know interesting uh oh yeah here um like something I was quite surprised by but in a good way so when you think like what is wasn't right because you know that it's some it's some assembly or binary format that that executes uh you know wherever you want it to so so yeah it is indeed an efficient binary format that executes on a stack machine uh which we're gonna get to in a second but actually if you take a wasm file and decompile it into like mnemonics uh you will see that it has a lot of parentheses and in general looks like like a list uh which you know some of you might find familiar uh I find it quite funny that one of the oldest programming languages still you know make Finds Its way to to the mainstream actually and um yeah this is purely you know and and fun fact let's say but the the way that it executes is actually really simple and uh similar to the languages like fourth or actually even the the early jvm um so if you have like an addition you you basically parse it into like say well uh seven plus five right you parse it into into first like the well reverse polish notation but then you start putting the elements on the stack right so if I have a function oh actually when you look at the function here you will see that uh first you have the params then you have the results so you first you know get the left hand side then you get the right hand side then you use the add function uh on on both of those so yeah it's like that when you're parsing this expression you're just basically putting the first upper end on the stack putting the second operand on the stack then putting the uh the operator on the stack now the whole execution is that simple and looks like this okay pop something from the stack if it's and if it's a function like an an operator we'll see okay how many arguments does it have okay we can see that addition has two so we have to pop two arguments from the stack of course here they are like concrete numbers if they were operators themselves we would just continue this this popping uh popping thing until we get get the result but here we just you know pop five and seven from the stack we know that now we can apply the plus to them and we have 12 and basically we execute until the the stack contains only only one result or actually well it's empty because we will pop it in the next step anyway so yeah uh this is this is how one of the most popular well uh assembly formats works and uh I think it's quite beautiful the Simplicity uh so coming back to to the Earth coming back to Ink um this is this is a comparison chart between ink and solidity is actually taken from the use of Inc website which which I recommend I think I'm linking it uh further down below but uh this is a comparison of features of ink and solidity and of course I I don't expect you to kind of like analyze this and uh and memorize it uh it would be completely you know uh crazy but what what I wanted to to convey here is that pretty much you know every concept uh is is shared between ink and solidity with some slight differences but unlike the level of ideas in the code and of of the concepts they are actually quite similar so you can think of it as a slightly different flavor and writing smart contracts and not something that's that's entirely new and you have to you know throw away all that you've learned no no you can you can still apply that you will use a slightly different type system or safer actually and you will have slightly different storage entries this is actually quite quite important to the speed aspect and and uh performance aspect because well the the 256 bits as the only storage entry by default in solidity and well in evm uh gives you a lot of overhead and um yeah I'm kind of glad that Inc chose the variable storage entry so if you need to small store numbers you can use small you can do small small variables so this this actually plays a big part in the efficiency of the whole thing but in general the concepts will be familiar if you're coming from solidity so you just learn a bit of you know new syntax maybe a bit of the news type system but nothing crazy and you're good to go so this kind of you know like debunks the myth of like oh rust has a very steep learning curve well rust rust may have that curve but Inc is a subset of rust and should be quite familiar to solidity Developers um yeah so now a more practical look at developing smart contracts in ink and again this is just to give you an idea this is not a complete guide by by no means uh for that I encourage you to go to the to the links that I pasted pasted before so the developer portal and the Azure documentation and actually the use of Inc documentation which is which is great I love it so but here to just give you an overview what will be required to to write your smart contract well of course you will need to install some dependencies some some tooling um quite obviously you know rust compiler and this is also install cargo which is like a package manager slash build build tool slash General utility tool for us and then you will uh you will install cargo contract with just basically an extension of of cargo 2 to also handle smart contracts in ink uh once you have all that out of the way we can create the contract so you know like it's it's a scaffolding or bootstrapping similar to what you might be used to from I don't know rails Django or whatever you used in the past um then you obviously write your code so the The Flipper code will be there written for you when you run cargo contract new which is quite cool because you will have the general structure of the code and you won't have to remember what macro you need to use it will just all be there you can just make your changes you can of course start from start from scratch and just build it out yourself uh whatever you think is the most efficient and then you will compile and test your code well first the cargo test command I'm using the plus nightly here to use the nightly tool chain to basically get like the latest greatest uh of rust packages which is required by by some of the libraries we we sometimes used for for the smart contracts in general I think uh for like pretty simple smart contracts you could get away with just using cargo test and cargo contract build uh we will actually look at the cargo contract build a bit later uh you'll also need the dash dash release argument probably um but but yeah so the effect of that will be huh yeah another time that the joke here so two artifacts the The wasm Blob basically the binary code the metadata and the two of us bundled together so you will probably almost always interact just with the contract bundle I try to you know include that I can off by one error because yeah that can be tricky um in in in programming uh I now realize that maybe it's not as funny as I as I initially thought but well there you go um yeah right so I will also show you how to deploy the contract to the network and interact with it but that's postponed until the end of the presentation just to keep the structure a little bit better um right so I I thought that a good example of for you know developing a smart contract would be the probably the most ubiquitous smart contract that we all interact with on well daily basis and uh that's going to be the PSP 22 token or erc20 it's uh well erc20 is the ethereum version PSP 22 is the is the polkada slash substrate slash all that zero version so the differences are minimal the PSP 22 is actually a bit well has a bit better error handling well let's see but uh for for the sake of this presentation I decided to call it the pitcoin it's not a real coin it's just something to to to make for you know for the for the demo um so yeah let's let's let's get to it uh this is this is the erc20 standard and uh you can see that it's actually very simple right uh so this is this basically tells us what an erc20 contract needs to be able to reply to right so if you ask you you need to be able to ask it for its total supply of course balance of a certain address uh actually let's let's skip allowance and transfer for from for now because it's uh yeah uh we're gonna keep the shortest week uh then you will be you need to be able to transfer some tokens some amount of tokens to a certain address and uh yeah our contracts will will return uh better uh result like not not a Boolean value but uh but but the result we're gonna you know touch up on that later um yeah there's also a proven transfer from which is like you can uh basically say that someone can spend your tokens on your behalf and how many tokens they can spend uh you know it's it's dependent on how how many tokens you actually approve and then the transfer transfer from is the method for transferring the tokens on someone's behalf right the events uh of course they they the contract emits events but uh we haven't explained that yet so let's let's leave that for later and just move on right so of course the the like I said ink is an edsl so it's rust with some macros that you need to include uh you don't actually have to worry about remembering the exact syntax here because when you type cargo contract new in your shell the all of this will be generated for you you'll be just in charge of adding the logic and uh yeah basically the the logic and the tests maybe uh so the macros in general start with well at least for the sake of this discussion start with so with like a hash or how do you call it um so the the first one has to do with you know in contracts not using the the regular standard Library they have their own little standard Library uh just to you know make it compiled to awesome easily and uh not be bloated or anything uh then there's the in contract macro um oh yeah uh so you put the in contract macro on the module and uh a module is basically a container for some items uh like you can think of it as a namespace if you're if you're familiar with C plus plus maybe with some a little more expressive than the namespace actually I would say um and use is well I want to say an import but it actually is not an import it's just introducing some binding a name well like a longer path to a name basically so this is actually quite similar to what you get in C plus uh so for example the ink contracts don't execute any code when when you when you do a use right uh so this is different than say python for example uh so in that in that sense it's uh it's a user not an import right um right so we're gonna you know like dissect this in a second but uh what the ink contact macro does is initialize some stuff initialize some aliases so that the development is a little bit easier but also it enforces certain invariants like the contract has to have at least one Constructor it has to have exactly one storage struct and uh I think it has to have at least one public message that it can reply to and so on and so forth we're gonna see complete examples in in a minute um so yeah I mentioned storage struct it's there down below and yeah this is what it looks like so we're gonna use a mapping to keep our balances uh so we need to well we we could just type out the fully qualified name here and the balances but that's just not really convenient when you're using it a lot so we're just gonna uh short shorten it and say use ink storage mapping then you'll be just able to say mapping because it will bring this into our current namespace now the important thing is the ink storage macro um which which basically what it does is tell tell the the compiler or or the change Rhyme Time actually to put the to put this particular struct in the in the contract storage so this this step will actually end up on the blockchain right and it's quite important to to be you know maybe not super cautious but but you need to think about what you're putting in the start because basically just we are you know by virtue of it being on the blockchain uh like I said out of zero is cheap but still if you store a lot of stuff like images videos and whatnot uh you'll end up with a very expensive smart contract so you need to be smart about this basically there are ways of you know like put uh put the heavy stuff off chain and just keep some commitments on chain um everyone wins about it in in the in the web so so yeah I actually have two slides about this so I'm gonna need to switch them uh in a better way so I'm gonna be talking about rust here a bit just to give you some some contexts if you already know rust you know you'll probably be bored but but hey uh this aims to Target a wide audience so let's let's talk a bit about rust uh so struct is basically just like a class uh but the important thing is that there's no inheritance uh so some some of you might associate inheritance with subtyping but actually this is not the case inheritance is just one way of doing subtyping and uh rust has its very own subtyping maybe not as easy to use as uh as in you know as in Java but but it works it's actually very interesting but for the sake of this discussion the structs don't have any inheritance which I think is a very good decision it makes the code so much clearer and actually structured a lot better uh inside these tracks we of course have Fields uh Fields have their types declared here uh by the way balance is one of the types that uh you know is given to us by by using the car the the in contract macro now we will be able to access the fields just like you would access a field of any of any object in an object-oriented programming language so you know track dot field so here we would have track dot total Supply or balances now the interesting part here is that is the mapping itself actually uh one of the most common data structures in in smart contract development I would say and here we are kind of like making like a very small Bank not really but but well this is a maybe a good analogy so we are well we need to keep the balances of of the accounts that are using our our contract right so the the mapping is basically from an account account ID so this is just a regular account on the on the blockchain of of choice in our case r0 and from the account to the balance and an important thing to note perhaps is that this account here is actually not some weird attraction it's basically the the the account number you use for interacting with with the blockchain so so yeah when interacting with the smart contract you don't need any additional accounts or you don't need to any you know need to do any specific actions you just you just can can interact with the smart contract uh provided you of course have some have some balance for the transaction fees um right the like I said the balance is is a built-in primitive given to us by the in contract macro then account ID is actually the same those are just type aliases for some balance is actually an unsigned 128-bit integer um if you're migrating from solidity by the way as an aside and you need the 256 bit integers and site integers uh you will need to use the library uh from from the crates IO website you can Google it very easily um right so moving on there's also the interesting derived default macro so this basically instructs the compiler we won't get very heavily into into the into the interfaces but let's let's say that what this does is allows you to write you know give me the default default struct like this and uh it will most probably the default implementation is basically quite natural for for most of the for most of the structs like you know whenever there's a numeric type the default is probably zero whenever there's a container of some Source or like a list or a mapping or an array the defaults should probably be an empty container so ntra anti-mapping uh actually if you're more into algebra the the default here is actually like the zero of a mono it if you if that intuition helps you in in any way and again once the once the struct has the default method implemented uh we will show you how to do it manually and later in in a slightly different context when once it has the default method the default interface implemented you can use the default method and uh and it will give you the you know like uh like a zero instance of of this one that you can safely use to initialize stuff for example right so for example when we're initializing the contract initially we'll create a default track that basically has everything empty um yeah so the the macro on the previous slide but what it does is basically tries to derive an implementation like that right so this is a rust implementation of a of a trade for a particular struct and basically this tells the compiler that you will be able to call them the methods from this trade for this for for the instances of distract so it only has one method that's default and it just gives you gives you back self so self in this context is basically default and what it does is it just creates an instance of the struct and it actually delegates the the default implementation to the to the implementations for each and one each and every one of the fields right uh fortunately both Total Supply and uh so so like the the balance here and the mapping have default implementation so we're good to go the compiler can derive it by itself you don't need to actually worry about it too much um yeah so the next thing that's important in the in the contract is its Constructor which we will put in the impul block so like implementation this is this is actually very similar to the to this one here uh but we're not implementing a certain interface for Bitcoin we are just implementing the methods for Bitcoin so it's just import Bitcoin and then then you have your methods right so the first one will be marked with the ink Constructor macro and uh like I said before the Inc contract macro makes sure that you have at least one public method marked with the end Constructor uh macro now in ink because we use the the macro and not just some special name like in it or whatever you can actually call this method whatever whatever makes sense like semantically um because of that we can have as many Constructors as as we wish and call them whatever we like so it is actually uh makes sense in in certain use cases to not call them new but maybe you know like uh enroll or whatever um and actually does it does it those are quite a simple thing of course leveraging our default method that we've learned uh on the previous slide so we just say that okay uh let's start with the empty mapping for the balances this this will just give you an empty mapping because no one has interacted with the contract yet uh we need to know the color because if someone is calling the contractor it's it's the owner basically of the contract so the color is well there's a special self common cone and method that gives you all all sorts of information about the the environment in which the contract is executing in including the color of a particular method now if I if I create this contract obviously I am the owner of the initial Supply right I haven't transferred it to anyone yet so we insert into the balances uh under the colors account we insert the total Supply uh which which yeah which makes sense because for now no transfers were made no no tokens changed hands so the the the the owner has all of the all of the supply and then we just instantiate this track actually using a shortcut because you usually we would write total Supply the field that's an internal supply column and would get the well total supply of tokens which we pass here as the argument of the Constructor and same for the balances but if the value you want to assign to a certain field and that Field's name are are the same you can just write just write the name and the compiler will figure it out uh it's a useful shorthand way to write this um right just to you know for educational purposes I wanted to show you the contractor how it used to be in ink 3.3 and by the way the ink that we're talking about now is ink4 um I'm not gonna say 4.

0 because it basically doesn't age well but it's ink 4 uh with you know all the new goodies but the the old way was actually super super educational and a lot was happening here uh so I'm gonna just you know very briefly walk you through it so in the olden days we needed to call this special initialized contract method that was actually taking another function as the argument and there you go you have uh this is how you write Anonymous functions in Rust basically uh kind of similar to Ruby actually uh but with types so you have the argument list in in like double pipes and then you have the argument names and the types and then you just write out the body of the function in the curly braces uh if you're interested what those ampersands here mean those are references and well basically in Rust we say that the reference is borrowing a value we're going to get to that but uh it's kind of similar to C plus plus references if you're if you're familiar with that uh but basically what it does is that like lets someone else access the variable for a second and not necessarily copy it over and just uh you know seed ownership completely if if we append mut mutable to the to the reference it means that someone we're giving this value to can actually mutate uh the value uh which is actually full and it's very explicit and uh if you remember what I said about you know making sure that you're quite careful about mutating your state and also about implicit stuff this is cool because it's harder to mutate the state and it's very explicit so so I think that's good um yeah so that that would be um that would be it more or less for the for the old Constructor um now notice how the self here is is actually capitalized because it's a and and has double columns yeah it's it's basically a static method so like a method that you you're calling on the on the whole class and not on a particular instance which which is important sometimes um right so I think right now we have enough information to move on to rust memory management which maybe is not you know it's not crucial for you to be the expert on this when writing smart contracts but definitely is a very interesting topic for for a workshop and uh of course we're just gonna scratch the surface or not even that but but still maybe it will get you interested in in this area and you know lead you to your own research of this like quite exciting area um so rust is using the ownership model uh which means that yeah just like oh I'm just going to read my slide each Heap allocated piece of data has exactly one owner you cannot access the data if you don't own or have been borrowed the the data right so and and we're well it kind of assumes that there are like multiple multiple people trying to access your data well think of it as multiple Scopes or multiple functions uh and so on um right so so yeah so the ways in which the the ownership of a piece of allocated data can be transferred is twofold so the first one is is move uh you might know this from also if you if you if you know it um so move is like yeah when you're passing something by value or or just assigning uh you're basically giving away the the ownership of the data we're gonna take a look at the examples later so like you're saying okay I'm done with this particular piece of data I don't want to use it or modify it anymore or even access it and then if there's borrow which is uh was is commonly known as passing by reference and borrow is like okay you can you can take my piece of data you can you can do some stuff depending on whether that's a mutable reference or unmutable well you either can modify it or not but surely you can take a look and then you give it back to me right and rest compiler is able to track the ownership of each piece of piece of data with basically compile time checks and and zero overhead so this is cool compared to like a model where you would have to manually model manually manually manage your memory access and also cool compared to a scenario where you have a garbage collector and it can affect your program's performance in an unpredictable way right so here this is actually The Best of Both Worlds so it's super efficient and uh there's there's no overhead but yet you don't need to remember to free the memory or else you will have a memory leak no this is this is like the modern modern day and stuff um yeah so who illustrated on a very simple example uh uh you know let's say that we have a we have a struct with just one field and then we instantiate it and we say that there's a function where we pass pass it by value so just you know no reference is no no anything okay so we pass our struct by value and what this function does is just increases the increases the the number that's that's inside this track right so so what what do we expect right it looks simple and and in in a lot of languages this would work just fine right but here we got a compilation error um so it says okay it's borrowed and moved value okay uh We're Not Gonna perhaps get into the details but well you basically can instruct the compilers saying okay if someone wants to move this uh you can actually copy the value over but but yeah uh in this case well the problem is that once we move it to this function we no longer have it so we actually are moving it here right here this scope has full uh the the scope inside of main I'm not sure if you can see my cursor and here we are completely giving away the ownership so now when we try to access it in the in the line with print line well it's no longer ours so we the compiler tells us okay it's no longer yours to to inspect like no no you cannot do that right and the rest compiler actually is quite uh helpful uh with all all those messages because it basically tells you tells you what to do okay um now we can fix this well we'll see if we can change this but uh we can fix this by using passing by reference so borrowing right okay so instead of just writing Foo we say this Foo here will be will be borrowed it will be passed by reference okay so yeah now it's good uh we can see that no move occurs and we should be able to use this uh this Foo here and access the field in the in the print line right should work but remember what we what we really said about mutability you need to explicitly say that I want the borrower to be able to mutate this this reference the little the contents of this reference and again notice notice the rough compiler telling us okay consider changing this to be a mutable reference okay yeah this makes sense right this is in line with uh with what we've been talking about so let's do just that and we have a mutable reference and actually when you're passing by reference here you need to also note that this is uh this is me mutable reference here right and this compiles without problems and and actually even will display uh the value of two here at the end um yeah in general I would avoid mutating data as as long as you know your use case allows it because it just it's it's easier in all aspects but uh but sometimes you just have to and then you have to remember about the mutual um yeah by the way it wasn't is heavily optimized towards working with references so sometimes what people do is basically they will take the the function and like delegate to the same function as the one being called but with the arguments changed to references uh it's actually it's it's fun stuff but you'll see this quite a lot in the wild let's say um okay so let's get back to uh well after this longer side note let's get back into our smart contract and well messages so messages is well it's maybe not the best name I guess it's uh kind of you know taken from the object oriented programming World maybe where some people are calling methods messages but this is basically like the public interface of your contract so something that a user using the you know graphical user interface or uh or some client or or actually an application um will be able to talk to your contract right so the first type of messages is like asking about the contract state so the first message that we can ask the contract is obviously the total Supply and uh yeah this is really simple you just you know um this is a public functional method actually that takes the self as the first argument uh interestingly the methods need to have self-cast as a reference um and passing self as the first argument is basically similar to what python is doing you just you just want to make it explicit right some languages just give you the self implicitly but we like to be explicit so this is the philosophy and rust it's actually it actually makes a lot of sense and uh it we just say that it returns a balance so this gives you just the total Supply so the total amount of tokens in your contract right so second type of a message is the balance of a certain account and this this illustrates actually a very very very important concept uh so obviously we will be getting the balance of a certain owner which is an account ID now remember our balances uh it wasn't mapping so when when accessing the mapping basically we get two options either the key so the the owner here the account ID is in the mapping in which in which case it's cool we just return the the result or it's not in which case well some languages would throw an exception some languages would uh throw an error uh or write some message here they return like an option option value saying okay there can either be something inside or there can not be and uh we're using the unwrap or default method to say basically okay uh so whatever is in this result if if it's like a success result so if the if the key was there give me the value plain value and if it wasn't give me the default actually instead of some error message give me the default and the default for the the balance is zero as as as we remember so notice how this this like simple concept actually makes our balance of method very very expressive and actually makes it work with the whole like accounts model of of this contract so let's say someone has never interacted with the contract so what what balance they have they have zero right if I want to transfer a hundred Bitcoins to to someone who has never used the contract if this was returning an error I would have to devise all this special Logic for what happens when someone is you know has never used the contract before and there's therefore it's not in the mapping but if I just say okay it's zero if they're not in the mapping I can just add it and it just works so well yeah so uh I really like this well algebraic error handling in general um now here I wanted to make this this infographic basically because I well I think the presentation looks way more professional when it has contains an infographic uh I'm not very good at it by the way and this concept is so simple that it really doesn't require one but well there you go how do we make a transfer uh using our you know balances mapping and the whole model that we've devised right so let's say that Alice noticed that this is regular Alice not malice like in the hack case um her balance is a thousand Bitcoins and she wants to transfer to Bob a hundred of her Bitcoins right okay so what the contract does is well obviously it needs to check whether Alice has sufficient balance well obviously she has so what are we doing now is well we are decreasing Alice's balance by the transferred amount and of course increasing Bob's balance by the transference transfer the amount and of course well the the balance is reflected at the end of the operation and also note that by by virtue of our unwrap or default implementation of balance off if I always use balance off to send this to Bob I can always say well balances well uh balance of box account if he has never interacted with the contract before at this stage it will just be zero so I can also safely put whatever balance was plus 100 into the mapping and it will work so this is this is why I quite like it so yeah the sending and transfer is is quite easy but well there's some code uh mostly you know like another very interesting boilerplate but uh nevertheless we're gonna we're gonna inspect it so the first thing is here um here this is the like the the actual message and remember what I talked about about delegating to uh the same version of the method but with references so this is exactly what is happening here uh we are also kind of well we don't want the the user to be able to to be to have to provide their own account uh because this kind of figures from who's calling the who's calling the contract so yeah we will do just that we will figure out the the caller's address and we'll just pass it to you know the actual transfer method as a from and yeah so it does basically the thing we we've just explained but first it needs to check the balance of whoever is sending the the transfer of course if the balance is less than than the value transfer then we return some error and we are actually gonna talk about what this this error here means but if all was good we we proceed and uh to the balances we of course first decrease the balance of the of the from from sender um obviously you know we need to decrease the balance we we had here uh using references just because this is the implementation of the insert method no need to worry about that for now if if you forget about the duras compiler we'll just uh we'll just remind you in a very gentle way so don't worry about it of course the two balance is the balance of the recipient so yeah you know we use the same balance of method um now notice again that if the two account was not in the mapping it's okay because this will just return zero right and we will nevertheless we will insert in under the two key we'll insert the the previous balance plus the transfer value so all good now we are also emitting an event and returning okay but uh about that later um how do we handle errors as you can see here if something bad happens we are returning an error of some sort first of all we start with defining our own custom error struct we can in theory represent the errors as strings or actually any other any other type you would like even like numerical codes but like the clean way would be to define a well strike actually an enum so think of it as a variant type so this enum can be constructed with one of the several Constructors depending on what is the what is the the actual error like in our case from the transfer if the amount you want to send is is more than you have you will get the insufficient balance error right um now rust has this way of kind of algebraically handling errors so no no exceptions uh no weird execution flow breaks uh we basically Define another enum that I'm calling it my result just to you know not introduce any conflict with the actual result name that's that's already in the rust standard Library well actually in the language but uh but the idea is exactly the same so the my result can have two variants either and okay well sorry if so if everything was okay and an error if something was wrong now it's parametrized by two types so obviously the result is like well generic or or polymorphic uh with regards to the stuff that we can put inside it right because there can be like my result that returns an integer as as the good value and maybe a string as the error or it can return um like some object when it's okay and our customer that we've defined on the previous slide when when something's not okay now we can we can also do some type aliases which is quite interesting but also it's important to note that this is not type check so this is just a different name for the same type it's not like the compiler will not be able to distinguish between my error written like that and my results written like that but this is just a just a convenience uh tool for you so so yeah so this is how we are going to represent our errors right we are going to return my result uh colon colon okay and everything was okay and my result uh common error if if it was not okay actually we're gonna use without the mic we're just gonna use result and um and yeah uh you can actually notice that this is way more expressive than just returning you know like a Boolean value true or false whether the transfer succeeded because this first of all this carries the information what actually went wrong uh second of all the the algebraic part that I I mentioned is it's not some special uh language construct and doesn't interrupt the execution flow you can just treat it as another value and act on errors as like part of your normal normal programs logic right which is which is quite expressive and quite cool um now another thing we we talked about are well we've seen like a teaser of uh in the in the specification and in the transfer implementation or the events so events are basically how your contract will communicate certain events to the outside world right so just uh maybe wallets maybe some indexers maybe some explorers uh now it's just a struct with with some fields of your choice obviously you want information you want to make available uh marked with the Inc event macro we should be used to the to macroing important elements of the contract code by now and for fields that you want to be like indexed so if you want to search using this field you would probably want to use the topic annotation topic macro uh we well here we want to probably index the to and from addresses and but not the balance because well the the balances of the transfer will be all over the place so just like in a database you wouldn't necessarily do an index on on this one but ah it will it will vary from use case to use case now the interesting thing here is you know why are the from and two fields wrapped in this optional struct right it's our well my intuition would be that the transfer should always have a sender and and the recipient but well this this can be left as an exercise um the way to emit an event is just by again using the and Method and emit event and you just need to instantiate the struct and by the way the the asterisk here is basically the inverse of the Ampersand so it takes a reference and gets a concrete value out of it um now this way of emitting events uh will probably break if you use multiple contracts and cross-contract calls uh in order to avoid that there's some workarounds you will find this in our documentation that that was linked before um now you may remember from the explanation of the Dao hack that we talked about how you can actually transfer some some actual chains native currency into the contract and I I was saying that solidity has this receive method so Inc has has a different one different way even of of handling uh like payments in the native currency so you are able to basically transfer some value alongside with the call and uh which is which is quite cool um you can just you know whenever you of course you need will need to have the you will need to have the message here just to make sure that it's you know callable by by your users um but also you need to Market this payable so that so that the contract will know that it can accept the you know native currency as as part of the part of the call it can be useful for fees or stuff like that uh now it's also important that you can answer well you can access the transferred balance using this again the method from self self vote and and uh perhaps an important notion here is that the contract on on rf0 but on actually on the substrate chains is is an account it has an account just like anyone else so therefore it has balance right so it can accept the currency and and so on um right now another important and maybe quite um uh delicate aspect because it's quite easy to get it wrong and if stuff goes wrong it usually goes wrong on this in this area I would say uh cross-contract calls so basically a contract calling another contract uh like for example at Dex we'll have to transfer PSP 22 tokens so it will need to call some methods from the from the token contracts right now there are two ways of making a call to another contract one is uh more straightforward and concise the other one is more low level and well uh a bit harder but it does allow it does allow if you remember from the previous slide to send some native currency alongside the call so uh pretty much I would say this is the only case where you would want to use the the low level way uh I guess yeah I guess in general the easy way should be and like type checking concise way should be preferred unless you have a very good reason uh to to use the more more powerful version but but the harder version uh but well ultimately it's up to you of course um now the first version the the easy way uh the the the in-contract macro will basically generate a ref for your contract so this will be like uh like a facade if you will uh allowing other contracts to to access certain methods from your contract right so importantly it will be generated for you but in general it won't be exported so it won't be visible from the outside so you need to re-export it like this right uh I recall how use allowed you to bring stuff into scope problems also like re-exports them to the outside okay um the color contract will basically just try to just try to map the reference into uh yeah um here we also kind of try to initialize the reference from uh from some account ID this is yeah this is important because a reference itself well says what what is the type of the contract we're calling here so basically what's the like the the class but it doesn't tell you doesn't tell the compiler what's the actual instance of the contract that we want to call so in order to tell it what's the instance we basically need to somehow initialize it with with an account ID of a of a specific contract uh now the important thing here is that when you're imparting the the reference here the rust compiler can actually check if if what you're calling makes sense right if the type signature match if the if the method exists um yeah and the the ask here is basically a uh a cast as you might know from from other languages um in theory it shouldn't be it shouldn't be required but the inferencer is basically it it has a lot of work to do as is and sometimes it's it's a little help um yeah so now the the way of just building a raw call which allows you to to transfer some some value to the contract I'm not even gonna you know like dive into the details we also have a guide on that that's uh that's much more detailed because there's uh oh there's a lot of stuff to to take care of but the general idea is that you use a code Builder and um you of course need to say what contract address you're going to be calling then once it's going to be the input to the call uh the idea of selectors is probably out of the scope of this presentation but in general you can you want to have like uh well a number associated with each method so that you can like transparently tell the execution input what what method of this contract to use and uh of course you can export the selectors with the pub use so that you can just use aliases and not you know magic numbers and then you push arguments uh I mentioned the transfer value and uh the you need to say what's the return type note that this will be wrapped in one more layer of result because the call itself also uh can fail and uh yeah and then you just basically fire the call so it's quite cumbersome but if you want to transfer the value that's that's what you're gonna do um oh I actually mentioned the selector already but this is this is a way of assigning like numbers to the methods that someone might want to call and uh basically you can then make it as a as a constant and Export it so that you know the colors of of your of your method like remote colors uh won't have to guess them or uh or put them as as you know like number constants in their code which is usually a bad practice they can just import what you virtual export here and uh and just use it and of course you Mark a method with a selector or a message uh in a similar way to marking it as payable but you can you know you can put the value value here as well um now let's say we have we have already created our contract now we need to be quite careful with uh deploying the contract and and in general executing it on chain uh because basically there's a lot of a lot at stake right so of course the first step is to write some unit tests to test like the you know the fine-grained uh functionality of your contract but mostly like the trivial stuff but uh some invariants that you want to hold or maybe you know here we want to change it if we actually make the transfer uh do the account balances uh do the account balances decrease and increase as we expect and so on so forth so uh yeah they they are just like rust regular unit tests you can just use some in-gift in and uh tests goodies for you know dealing with default accounts and uh simulating events and whatnot now the important part is that this is running on chain uh well sorry on your own local machine and not on chain as as you know in contrast to the deployed smart contract right so this is cool because this allows you to iterate way more quickly um you also have uh you also have end-to-end tests now with ink you will find that in the guides but uh but yeah I think this is outside the scope of of this this presentation so of course the unit tests and the end-to-end tests off chain are not enough and uh if you try to deploy your contract you first will deploy it on on the test net and only after testing there for you know sufficient amount of time with with real users with doing like load testing and performance testing and trying to look for pitfalls and Corner cases and of course getting an audit only then you will you probably you know uh go to mainnet and try to have actual users involved uh in that uh so yeah this is just you know uh good good practice um now you will you will notice that we are working quite a lot with the result types and you may you may think that this is kind of repeatable and uh actually a lot of boilerplate now uh what we want to do here is is basically somehow uh collapse that into something way more readable like chain the chain The Operators because we can see that you know uh both methods return the same type the errors differ slightly but but that's okay we still would like to chain them in some Pipeline and do it like that uh seems like to defy the whole purpose of of actually this this whole algebraic result handling right so well if you just unwrap it and panic every time we see something bad now this is not the way to do it so rust has this very handy uh question mark operator that okay provided this one thing holds so you are able to convert from one error to another uh so like you can convert from my error one two my error two three and similar from my error one into my R2 and oh sorry uh yeah if you can like find a common type of errors and convert each error to to the overall result of the function you don't need to like unpack every value you can just say okay uh this is the value that might fail but let's treat it as a computation that does not fail and if at any point in the chain of question marks this computation fails we will basically return an error result from the whole computation but inside this function you can just treat them as functions that don't fail like completely pure functions but you know provided that you can you can do a conversion some common error types already uh have this conversion implemented in the in the library some do not in which case you'll you'll have to write an implementation quite similar to the default implementation we did in a sense that the syntax is essentially the same you just need to uh say you know that this is the from interface for a specific parameterized by a specific error and of course for another specific error now uh yeah I I generally am very scared of doing live demos so uh I've done some screenshots but maybe we'll get some time to do to the live demo as well um so so yeah basically um basically what you do is you go to the contracts UI and assuming you have ran the cargo contract plus nightly build release uh comment you will have in your project in the Target slash Inc folder you will have the contract bundle and yeah here under the add new contract you will choose the account to deploy from it will choose the contract's name and the bundle it will get validated if this is like an even you know uh correct correct file um well correct in a sense that it doesn't it's not corrupted um then you have this whole UI that will let you you know uh say what parameter well basically you're calling the contractor here right so you're saying what initial parameters you're supplying to the to the contract you get some gas estimations which is actually uh quite a quite a handy feature uh you can you can use some more advanced features but we're gonna skip on that completely because in general you're you're good with the defaults and um and yeah notice that this does like a small dry run of your of your contract so you it can it's it's also you know it can tell you whether this instantiation will be successful or not it might not be for example if you don't have enough uh funds to pay for the fees um and also by the way note that on the other zero test that you can just get t0 from the faucet so you should always be able to experiment freely and uh yeah um oh actually notice that this balance here it's it's not the balance in t0 it's just uh it's showing us TV review just because we use balance in the Constructor and it kind of assumes this is the change local currency but this is actually our well people coin in this in this example this is just what we are initializing the initial Supply with we can pick any number we like it's completely unrelated to the chains uh Native currency yeah so you will basically get a model like this uh you might want to copy the code hash uh well sometimes you might need it but okay um so now we have the deployed contract and we can choose the message to send to the contract uh actually notice that for for like Getters so the methods that do not actually change any State uh the this doesn't need to to to be an interaction of the chain it can just give you what's what's under there I mean like you won't be making a transaction here or anything um now you can also view the metadata which is part of the contract bundle you uploaded and uh yeah important thing is that the metadata contains the the stuff that you've put in as the dog strings and uh so you probably want to be quite uh you know you you want to make your your comments or dog strings quite good because there is a possibility that someone might need to read them and uh interact with your contract based on what they read here so it's it's better to make them quite readable and informative um yeah we can you know if we have more than one account we can uh we can transfer between those accounts of course well you don't need to own the second account you can transfer it to whomever you like uh but for the purpose of the test I just basically made two accounts and uh and yeah similarly to actually instantiating the contract here I'm choosing the the method choosing the two arguments and the dry run is here saying how much I'm gonna be charged for the gas fees and then I'm calling the contract and I'm seeing some some logs and events which is um yeah which is basically how we interact with the contract and um well we can actually very quickly go to the go to the terminal and I have a I have a contract here uh that's basically the same Bitcoin that you've seen before uh we can actually inspect it lib RS yes more or less the same but with some additional comments and uh maybe some more functionality now I'm gonna just build it uh oh well there's no changes so it was pretty quick and actually didn't do anything but I can check the target ink folder and you'll see that I have the contract file here so okay I know I'm good I'm just gonna go to the smart contracts UI uh well I already have it deployed but it actually doesn't matter too much because I can deploy the same contract over and over again uh I I'll just choose different names and I'll be all good right um make sure that you're on a zero test net you can of course use the ls0 mainnet if your contract is audited at and everything but uh yeah for the purpose of the demo we will use the LSU of testnet so I'm gonna click add new contract and upload new contract code well my my account is here um by the way yeah you'll probably need the the polka that extension or some other uh like signer like this but I'm using this one um contract name and we're gonna call it Pete coin two and the contract bundle uh oh I already have this conveniently in my well tsp uh this is not good oh sorry ink examples and here is Bitcoin okay so Target Inc Bitcoin contract Okay so so yeah we have some some metadata Parts the chain knows what it's what the ink version is 4.0.1 uh you have authors just from parsing my cargo tunnel file and then yeah then we can deploy the contract total Supply will be whatever I want uh huh and we can just upload an instantiated of course you will need to sign the transaction and there we go we haven't we have a contract like we said we can inspect the metadata it looks slightly differently different to what was on the presentation because the the other one was using uh night mode and and in the interact we can yeah check the total Supply check the balance of my own account as we've mentioned previously my own account has the whole Supply and we can of course transfer something let's use my second recipient and transfer them a thousand oh actually I could even transfer it to to my other contracts just because they are accounts as we said previously uh it's basically no transfer will actually be made it will just be an updated well no a0 or t0 transfer will be made it will just be an updated entry in the balance mapping as you remember but we can send it here and sign again and yeah some the contract is by the way emitted an event which is which is quite cool which is what we expected and now if we check if we check balance off test recipient yeah it's it's denominated in different units but that's basically the uh the value was transferred so we're good let's get back to the to the presentation and since this is mostly all I wanted to to tell you about it's time for some closing remarks with uh useful Inc projects that that I especially like and this is one of my absolute favorites the incathon by by a friend Dennis Soma and scilabs so yeah this is like a boilerplate for for Ink Smart contracts with uh you know full stack so basically you get the contracts you get the back into the front end uh it really can kick-start their development and yeah if you want to start quick I definitely recommend it uh another thing that's I think it's a must if you're if you're writing Ink Smart contracts sooner or later you you will either you know use it yourself or just uh encounter this in the wild so open brush is basically a smart contract framework but you can think of it as a set of you know like batteries included Primitives for creating tokens so if you want a PSP 22 token you just create a struct and import the PSP 22 trade implement it for your contract and you don't actually have to do much if you want to extend it with some functionality they also offer like a lot of extensions you can just import them Implement them for your contract and boom then it's mintable or burnable or has access control so this is this is an incredibly useful resource and a very widely used um you can also give a try to Seoul to Inc which tries to migrate solidity contracts to to Inc yeah it's it it should be it should actually work quite nicely for smaller contracts but I should give it a try um definitely a very interesting tool so yeah that would be all and uh if you if you made it to the end here's one very virtual Bitcoin like I said it doesn't exist uh oh actually it does but it has absolutely no value um so yeah for you and um you may take a look at the at the special assignment this is like a set of challenges if you want to really learn how to you know interact with the contracts and then really dive deep and maybe become like you know uh proficient smart contract developer this is this should be quite interesting and um yeah that's all for today thank you for for listening I hope I didn't bore you to death and let's hope I will see you on the Alex zero Discord and uh and Twitter and Telegram and let's hope that you'll build some cool use cases for for us to you know take a look at so thank you very much and see you around

Automatic transcript — names and jargon may be misspelled.