Build Dapps with Solana and Arweave

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey guys another day another video so before we get started quick update on the channel so it turns out i'm gonna be making a series of videos for the solana guys um these videos are going to show up on their channel and not mine so for a while i think the videos on my channel are going to be kind of coming in maybe sporadically i do intend to keep going um eventually i'll probably end up doing something with nfts just because it's so hot and stuff like that but uh just want to give you a quick heads up and today's video so today's video is the third in the series so the first video was the introduction the second was the how to connect to a wallet and send money so this video is going to be um basically a simple chatting application so as you can see here we have two windows on different browsers i needed to do that so that they can connect to different wallets so you have the wallet per um each browser up here on the top and then you have any ongoing messages that are already here between the two and then and i'll explain this as we move forward but there are separate dedicated accounts for the actual messages to be stored into so my chat message is of course um this specific guy's um account for holding chat messages and then the destination would be this guy right here and vice versa so i had already sent a message for testing so if i send another message i have to approve it because it's a transaction so there's a small cost so the wallet forces me to approve it and then as i mentioned last time all of these messages are being saved to r weave because as you can imagine you know over time this could be a lot of data so you don't want to have to pay rent on this to solana or um hold a lot of uh state uh lan ports in the account so that's what's going on there and then you do need to refresh manually because i haven't gotten around to creating some kind of polling mechanism that will auto refresh the messages but as you can see this works fine and just be aware this is strictly a demo sample application in no way am i suggesting that um you know this is the way that you should create your app obviously if every time you send a message you get alerted and then you have to approve it in order to send it it would be a very cumbersome chat application but um there's actually a lot of complexity here there's a lot of things you have to know about especially serialization and deserialization and the mechanics of interacting between solana and arweave or really any other decentralized data store so if you went ipfs most of the issues that i'm about to show you in this video would have to be resolved in similar ways over there as well so this is strictly about learning more in depth how solana works and also throwing in our weave in there as well so let's get started okay so as mentioned we are going to be integrating solana and our weave so i'm assuming you've watched the previous videos and you've set up your development environment you've got node you've got the solana cli you've got the local runtime running i'm currently running [Music] the local version of the network of course you can run on devnet if you don't want to run locally this is my logging screen this is my regular test validator execution screen so i'm assuming you guys have set everything up for um as per the previous videos so in today's video we're gonna need uh the test weave docker installed so basically what this is is it's a project on github so you would have to clone it let me see if i can get a better um okay so you would have to clone it right and then in the command line let me just show you what it looks like so you would clone the project in the command line you would execute the project [Music] the execute command is this right here right so this is a docker project so that would mean you need to install docker first right set up your docker environment first and then execute the command to get this particular project up and running i don't show how to install in my videos i think it's a waste of time i'm sure you guys are smart enough to figure that out so go ahead and take care of that first and then when you're ready you can come back and we'll take a look at the code so assuming you've done that this is the project that we're gonna build and uh i'm gonna make just like the previous two videos i'm gonna make this source code available um i'm not gonna make it available immediately i'm gonna wait until i get a few you know a few hundred views or so first on the video and then i'll make it available but basically this is a of course an npm project it's a create react application project slightly modified the dependencies are going to be similar to what we had in the second video but of course we're adding the r weave and we're also adding the test our weave sdk the r weave um project that i showed you will not install this so you need to install this separately so make sure you're having all of the dependencies that you see here and then i created a separate folder called program to put in the actual rust based smart contract and we're going to review this project starting here first so that we can um understand what exactly we're going to have to pass into the smart contract so we're going to do that first but i wanted to give you an overview of the application structure on the client side so just uh again this is a sample project don't write code like this i'm just showing you um some possibilities here but basically in order to make things easier for you guys to find whatever is related to our weave i put into source r weave so anything that you need in terms of our weave just look in here and you'll find it in here um anything specific to solana i put into this solana folder so for example interacting with the wallet i'm using soleil.io you can use something else if you want and of course we're running locally accessing the program itself i have the program id right here of course this would be replaced by your own program id so on and so forth and then anything specific to react and component building i have in this component folder unless it's one of the root components which would be app or index right here okay so let's get started with the code as mentioned we're going to start with the program itself again program is solana speak for smart contract so up here we just have our imports the dependencies that we need and then again um if you recall from the prior video we have a specific type so this is going to be the schema of our data in our account in this case for demo purposes i wanted to do something a little bit more complicated so i'll explain what's going on here so this represents a chat message instance so because we are not saving this time around unlike our first video we're not saving the actual chat message inside of solana we're saving it inside of our weave what i'm putting into solana is basically a pointer of the transaction id the r weave transaction id and i'm saving that into solana as a record that points back to our weave okay and then additionally i'm just putting a um just for re record you know keeping purposes metadata purposes so just to uh clarify this is a string in order to be able to accept [Music] milliseconds from the client side because we're going to be using [Music] react which of course is using javascript and that is going to be passing in a milliseconds timestamp you can convert a date object in javascript into milliseconds starting from 1970 utc time so for ease of serialization deserialization i'm just taking advantage of that fact and this is going to be our actual chat object instance these traits being implemented here are the things that give the ability to deserialize and serialize our object instances now the complexity or part of the complexity that you're going to see here is that because we're sending in multiple messages right over time the data that's actually going to be stored in the account is going to be an array of these and you're going to see that that actually has a fair amount of complexity to deserialize and serialize especially when we look at the client-side code later so just be aware that and then what is all of this busyness right here so um if you saw my previous videos you know that currently the account um uh data in solana is not resizable so they're gonna make it resizable in the future but currently it's just an array that cannot be changed um according to solana so what i'm doing is i'm allowing for certain placeholder values dummy values uh for the um archive id and the created on id so i need to get some you know dummy uh values in there so that we can have the right sizes for serialization and sizing purposes or else you end up getting a bunch of failures and error messages and stuff like that so then these two functions are going to be for initialization of our data and i'm going to get more depth into what that means specifically but basically as you can see here we're creating object instances with the dummy values and then because as i said the actual data is an array of these object instances for testing purposes i'm just going to have up to 20 in there and i'm just going to initialize you know 20 records for testing purposes so then if we scroll down more we see the entry point to uh our actual smart contract which is a rust function and then as before we have a bunch of parameters that we put in the main one is the data that is going in as parameters to our account so some of this code is like from the previous video so i'm not going to explain it but if we drill down here's where the meat of the code starts so what's going on here is we take our chat message object and then we deserialize try or we attempt to we try to deserialize the instruction data which comes in as a byte array right so we have to deserialize it so that we can get the actual object instance out of it right so we're doing like an encode decode scenario right and then we're if this should fail then we're logging it and then we're throwing um we're sending back um whatever the error message is so technically russ doesn't have exceptions but anyway this is what the error object that's being sent out is about so then once i get it i want to log it just to make sure that i'm getting what i expect most of these log messages once you're finished testing you should probably get rid of them because even logging has a computation cost and as i mentioned in the previous video you're limited in terms of how many compute units you have to execute and again that's in order to protect the network so let me get rid of this thing so you can see more of the code so then fine what are we doing here so now we want to gain access to the data in our account so that we can update it right so how do we do that well as i mentioned the data in the account itself is actually an array of chat message records right so trying to call deserialization on an array of chat message records doesn't work so you have to use a vector which is effectively the same thing but it's a dynamic array of chat message records and then from that you can deserialize um an array of chat message records um so this is so that it maps one to one it's it's apple's tables so then if i do that um one of two things might happen it might succeed or it might fail right so here i'm using match which is the the the switch um which is rust way of doing a switch statement right so if this succeeds which is represented by the enum okay then i can get back and return the unwrapped data the actual stuff that we care about right so the vec the vector array the dynamic array of chat messages right if it fails as represented by this enum er then i'm going to get back the actual error now the error itself inside of an error could be any error type that you want as long as it implements error but in this case i know that i'm using borsch in order to deserialize so i know that it's a certain type of error so i'm going to check the error kind right um that to see if it's an invalid data and then if it is i know that this is the first time ever that this um smart contract was run and therefore this data will not actually have any vectors of chat messages inside of it because when you first create a an account's data from the client side with the systemprogram.create account calls um it will size it and initialize it but it will initialize it as an array of zeros so obviously that would never deserialize into a vector of chat messages so the attempt again on the very first try is going to fail and um assuming no other error happened then it would drop into this code right here so then i would take the opportunity to run our initializer function the one that i just showed you before and it's going to go ahead and create 20 records of chat messages and they're all going to have that filler that filler text of zeros so that we can have the right sizing and we don't have any errors during initializing in during uh serialization or deserialization now if something weird should happen that we're not expecting then we're just going to panic which is the same thing as throwing an exception in rust um and the reason why we're going to panic although in most instances in rust you don't want to panic and you don't want to do you know anything that would cause a panic but in this case because we're running a smart contract and it could potentially be dealing with you know a lot of money we don't want to run into a situation where we end up losing that money either through a bug um because we just kept our code continuing when it shouldn't have or some other issue so in this case it's one of the rare um instances where you want to actually panic right all right so moving right along let's assume everything went well we initialized so we're in this um state where we don't have any valid data but the the schema of our data the structure of our data is at least valid with those zero values so then what are we going to do next so what we're going to do next is we're going to take that resultant d serialized data from the account and we're going to make it iterable and in rust anything that is iterable is also mutable and then we're going to take the position of it based upon some code and what does this mean position the position and this is a terrible name but the position is the index of the array record okay so the the element index of the specific record that we found based upon some logic right here okay so this uh pipe p is um the beginning of a closure in rust right so an anonymous function so in here we check okay so the element itself its archive id is it equal to the dummy text value so in other words what i'm trying to do here is i've already initially i'm assuming that if i'm here i've already initialized the records in here to dummy values and furthermore this may be the second third fourth etc running of my program here so i don't know which time um i am running this program it could have been once it could have been the very first time it could have been 10 times whatever right so what i need to do in order to insert the the data correctly is i need to find the very first instance of a dummy record so that i can modify the dummy values that are in here and i'm going to get into more detail about why we need dummy values but so then if i am doing this for the very first time as you can imagine then this index value would be zero right because this as soon as it finds one record even though we have 20 in here as soon as it finds one record it drops out immediately okay and so if this is the first time i'm running this would be equal to zero if this is the second time i'm running then this would be equal to one and then two and then three and so on and so forth right and again i'm doing a hard unwrap potentially causing a panic because if this fails i don't want to continue right i want to die immediately so now why do i have these odd looking dummy records and why do i you know even need this stuff and then why do i need to fill in or or overwrite these dummy records well um actually you know what before i explain that let's just see the rest of the code what it's doing so once i get the index i'm able to find the specific element in this array of chat messages that i want to overwrite right so then i'm able to take that instruction data message that we just deserialized up here right and i'm able to set it right here right because it's the only record that i need to set i don't need to touch anything else right so then once i do that now i want to end code i want to serialize this array of chat messages right dynamic array of chat messages and i want to set it to my variables so that i can put it back into the accounts data right so like as i mentioned before the mechanics of doing this is not trivial so um like i said although you never want to build an app like this knowing just the the the various activities um and the calls that are needed is going to help you out you know regardless of what app you need to to create later on so so we got now our completed updated vector record in the form expected by solana's account data so that's what we have right here here is just another log message so not a big deal i just want to check that the particular element i care about has been updated correctly so then here is where we want to explain why we have these dummy records and why we're updating them instead of you know just creating a blank new object instance and adding whatever data we need to add so if you look down here you can see this range element right here right so this is taking our updated data that we just created up here getting its length and creating a range for our array out of it and range means start from here and end to here that's the part that i'm interested in that's what a range is right so then this data is just a short you know a shortened name version of our currently unmodified account data meaning our our existing account data before we change it so then we borrow it as immutable because you have to be explicit in rust so we do that and then we say in here give me the data from here to here which is the same thing as our updated data's length right so as you can see when we finally update the accounts data not only does the correct schema the correct data type have to go in there right for later purposes when we read the data when we change the data etc but it has to be exactly the same size or else this will fail okay so therefore because so technically the uh are we transaction ids don't change in size however the created on ids do change in size because like i said they're milliseconds so earlier in time is less and and future and time is more so they do change but we cannot have um changing data put in here because then the length of the updated data and the length of the data in the account record would be different and then this uh attempted insertion would fail okay and this is similar to um the same thing that we learned about in our first video right except with a more complicated schema so although in this case this may not apply to your app because you're not using our weave or you're not using you know their transaction ids but regardless you're using something and you're inputting something and more than likely that record um is not always exactly the same length so you need to um you know handle that uh in some way and the way that i figured out is is this way although if you can do it a different way and still maintain equal lengths then that'll work as well um but that's what this sort of insanity is about so then here i just want to confirm again that everything happened successfully inside of the actual data post the save so that's what this logging message is about and um yeah that's it so um now here's the thing if i had to now if i wrote this code by hand obviously i need to test it right i'm not going to just write something this complicated and it'll just work on the first try that's almost impossible right so i need to test this but um would i want to create an entire you know client-side application just in order to take test my program obviously not right so in this video we're gonna introduce the topic of testing the smart contracts so that's what i've created here and this is based off of their hello world sample so it's pretty easy to get started right now in rust you have to label it with this attribute annotation and then you create a module test um super means the current module which is this file right here and then some necessary dependencies and then this is going to be our first and only test right here okay so this will get you the current program id right the current accounts running accounts id um this is the key i don't know if we're using this oh yeah we are using it okay so this is the key for the account and then we have the lan ports which i guess for testing we don't need but here the most important part i'm doing the initialization again to get the messages right and i'll show you in just a moment okay right here so so i initialize so that i can create the account with a pre-initialized set of data okay and then once i do that i pretend that i have um an instruction data that i pass in from the client right so i pre um fill in the values again these lengths are the um the correct length this is 43 and this is 16 and that's why you see the prefix of three zeros i had to fill in um because this is only length 13 and then i create an instance of the chat message i serialize it all right until the sense is not working so i serialize it right so it gives me an array in here and then i take the accounts and i pass them into our call to the actual smart contract so you have again the program id the accounts and then the the fake instructions that i just created right here now once i do that i want to confirm that the data got put in as expected so in this case because we created our account fresh we create our account fresh every time we run this particular test right we know that after our smart contract runs that we just need to look at the first element okay so what i'm doing here is i'm taking that first account and this is specific to our particular smart contract okay depending on the logic of your smart contract it may not be the first account correct it may be the second the third whatever but in our case it's just the first account so we're going to borrow it again we're going to deserialize it again and then we're going to get that it would have been an array of chat messages but again we just care about the first one right so it's a single chat message and then we're going to take that chat message and we're going to look at or we're going to test the values of the archive id and the created on right and the way that we're doing that is we're taking the initial value the test value that we hard coded up here right so archive id and then create it on and then we're going to compare against what was changed or updated by our smart contract so we're going to make sure that that exact set of data was passed in by our smart contract into our account data right so we check these two are equal and then we test that this is true so there is no straightforward way to assert this line right here so i'm testing it right here with rust and then i'm asserting that this test is true using a cert right so i'm asserting that this value in here and this value in here are the same value right so if i wanted to check that this is false in order for this assert equal to be true or correct this would have to be false okay so here i do the archive id and here i do the test created on id or test created on field and then let's see if this works so in here i'm going to do i'm going to do this now where did i get this this script from or where did i get this command from so in the test code once i uh make it available you're going to see some pre-built uh mini scripts basically so the one that we care about is right here right so we're going to use cargo we're going to use bpf because that's the binary format that solan is using and we're going to use the the manifest that is in our program directory this guy right here right and we're going to force tests to run which again is this stuff right here and and that's what's going to run down here now the other stuff that you see is i just created a short little script to help you generate a wallet once you're ready to to um try it out in production you're gonna need a wallet so i just created a script to make it easy for you to do that so that's what these guys are for and then all of these are the built-in create react app scripts now the reason why i put this environment variable prefix in here to change from the default port of 3000 to 3001 is because the test weave docker image that runs our test instance of the r weave runtime the r weave network is on port 3000 so obviously that's a clash so doing this will tell um react to run on port 3001 so that's what that's about all right so if we run this script hopefully our tests will be successful okay so our tests are successful right so we've prevented ourselves from having to um build our entire client just to check our smart contract before we're actually ready because obviously when we test we're going to test and it's going to fail and then we're going to test again and it's going to fail and we're going to add more code and that's going to fail and then so on and so forth there's a lot of iteration that's going to happen during testing so for that reason and also just for security reasons making sure that your smart contracts are doing exactly what they're supposed to do and even when they get refactored modified in some way the tests are still passing otherwise you're going in seeing what the problem is updating the tests or updating the code whatever the case may be this is super super important to do you never want to have any smart contracts that you deploy into production without a whole series of tests backing them up confirming all of your logic if your app becomes successful and you're dealing with people's money potentially millions and millions of dollars you don't want bugs you don't want security holes that's the last thing that you want all right so let's get started on the client-side code and this video is focused on solana and then of course our weave so i'm not gonna you know spend a lot of time explaining react and react related stuff so um let's just get an overview of what we're doing here so ignore this i was uh just messing around with apollo we're not going to use it so basically what's going on is like the second video we have to initialize our wallet and i'm reusing a lot of that code so if you don't know what i'm talking about please watch the second video because that's where i show sort of the ins and outs of attaching to a wallet and sending money and signing transactions and stuff like that so what i do is i initialize the wallet on first load and then once that initialization is successful i get the wallet object back and i get the connection to the uh the particular cluster that we want to be on so then once i do that what i do is i create a chat message account so what the heck does that mean so um when you subscribe to a wallet you create a wallet account um you can it will create one single address for you and of course you can add multiple addresses on top of that but none of those addresses are going to have any data available to them the data is going to be zero so this is deliberate because in solana data costs money either you have to pay rent for having any amount of any arbitrary data or if you like you can stake a certain amount of land ports and become rent exempt but of course there is an opportunity cost because you can't then take that money and do something else like invest it elsewhere so because of that i'm assuming the wallet people will create you accounts that have data of zero meaning empty so we can't put any any records in there any of our chat messages in there because currently we can't resize the accounts uh the data inside of accounts this feature is coming in the future but it's presently not available so what i'm doing here is i created this function that will if needed create a new account on behalf of the user um which will have a certain amount of lan ports so that it doesn't get charged rent and we'll have a certain as indicated here a certain size of the account so that we can fit our 20 messages and i'm going to show you what this implementation looks like in a few minutes but once that account is created or confirmed to already be existing then on the callback we're going to set um the chat addre address for display on the ui and then we're going to try and get back um any messages that are available in the um in those accounts related accounts so that would be my account right here and then the destination chat address which would be the um this guy right here right so he would have the inverse showing up on his screen so so that's what this is determined to do and the implementation is to get the history of received messages and the history of sent messages per the addresses that are related and then to get an array of objects specific to that and set lo local state so we're going to get into the implementations of all of these functions right now so so as i said before to make things easier for you guys i'm going to put everything related to solana specific to solana in the solana folder so you don't have to hunt around um and i'm going to make the source code available after i get a couple of hundred views on the video so this function is going to need a connection this connection represents the cl the specific cluster that you're going to um the current user's wallet the space of the accounts data that we're going to allocate for again the bigger the space the bigger the rent or the bigger the rent except exemption if an account um if for some reason while you're testing you need to uh reset meaning even though you've created an account already you just want to create a new one so you don't have any of the older data in there then i just provided this convenience parameter that allows you to do that but it's defaulted to false so in here you know we skip that and then we go and we try to find if an existing chat account has already been created and we base that upon the user's wallet main wallet address so the wallet address is the key and then the value of the chat accounts public address is the value of what's being stored in local storage so this means that after you run this you create the chat account every time you refresh this screen for whatever reason you don't have to go through the process of recreating the chat account all over again it will just go to local storage use the user's wallet address find the matching chat account and then you know assuming that exists then it'll go ahead and um just return it return the value okay now if any of this fails or you want to do a reset um then it goes through the process of actually creating the account so a lot of this stuff i did go over in um the previous videos you might want to take a look at that because i'm i'm going to kind of burn through all of this stuff pretty quickly so in this case we are going to create the new chat account based on the user's wallet address so we need a seed in order to do that so this is our seed this says who's going to pay for this particular transaction how much land ports this new account is going to get again we are using rent exemption because i prefer it over paying rent either way you need some money in the account because if your lan ports go to zero on the next epoch the system will try to collect rent and if you have no money it will basically disable your account and you will never be able to access it again so the size of your account and then the program id that is allowed access into the account then we have the previous stuff about how to create a transaction um set up the payer stuff like that um the signature for the transaction some of this apis has gotten updated recently so even if you say started working with um the web3.js apis maybe about a month and a half or so ago um a lot of the apis have changed in terms of the implicitness of signing the transactions and setting the payer and stuff like that and the the previous block cash so now it has to be done explicitly whereas in the past it was done implicitly so you definitely want to um check that out in the code later so uh once i create my new account so that i don't have to refresh it every time again i save it into my local storage that is the storage inside of my browser specific to a particular url this is the key this is the value that i'm storing and then i return the key all right so then we go in here and then we see how we once we've set certain messages as i said before in the beginning of this video we're not actually putting the real data in this case the messages and the created on timestamp inside of solana we're just going to be basically putting in pointers and metadata that point to r weave and then the real data quote-unquote is going to be inside of our weave so in order to retrieve the data we have to do the reverse so we have to go into solana we have to find those pointers those transactions i transaction ids and then we have to point back and query into our weave to get the actual data back so if we go into this function get message received history so i created a helper function because i realized that get message received history right here and get message sent history are using 90 of the same code so i created a single helper that each of these guys would call and this helper is doing all of the actual work so what is it doing it's getting the specific address that we're going to be pulling the records from right so that's here i should have renamed this to something general but i i think you get the message so i get that account because again data lives inside of accounts the main element inside of solana is an account and then assuming i get something back i go here if i don't i throw an exception and immediately drop out um i uh where is it i get that data back okay but before i can actually make use of that data i have to convert it into an into an instance of the actual type so as you can see here as i hover over the data is in the form of a buffer a memory buffer like a blob basically a binary blob right so uh if we go up here you can see that our actual um types for our data is not a blob it's not a is not an array of of uh or or or some other you know binary format it's actually um a javascript type right because that's what we need in order to be able to you know reference its members and and access the data right so if you look at this this class definition right here you can see that it's pretty much similar to our struct representation inside of the smart contract right now in for us it's a struct because there are no classes and rust but effectively we're trying to map this class from the client side to this struct object on the program side right which is why we need all of this deserialization code that you just saw and i'm gonna i'm gonna get more deeply into in a moment so not only do we need that the type um we also need the schema and i explained this in the previous videos so if you don't know what i'm talking about please take a look at those and that's the form that we need to do a decode into right so that we can actually um call into it and access its members so that is what all of this busy code is about now initially to tell you the truth i was trying to use borsch because there is a d serializer inside of borsch but borsch is not documented like at all and i couldn't figure out you know how to do it so then i had to resort to using buffer layout right here and uh by the way the examples inside of um the the examples from solana the solana team like hello world and all of that stuff were initially using buffer layout but then they switched to um borsch but the issue is in our case we're now dealing with an array of items as opposed to a single object instance like we were in the previous hello world example project so porsche i couldn't find a way to deserialize an array of that chat message type i don't know if it's not i don't know if it's not um doable or i was just doing something wrong but basically it wasn't working so what i've done here is i've used a buffer layout so what is buffer layout buffer layout is a tool inside of javascript very similar to borsch that will help you do a mapping from one programming language binary format into javascript that's basically the same thing as borsch but again it wasn't working so in this case this code looks very complicated but it's actually not that bad so what i'm doing is ultimately i want to create a struct a struct that represents the object coming in into our set accounts data right so i do that by acknowledging each of the two variables the member variables archive id created on acknowledging the type that they are so this is a c string this is this um an array of cat characters um as presented uh by the c language but it happens to work in rust as well now how do i say this it's not this straightforward to simply type that out give the member name and then create our variable because each of these binary formats have their own way of actually um spacing out the data in in the stack or whatever the memory that they're using is so on top of that you're going to have to put some sort of padding in between each of these members because on top of the type and the data there's going to be some metadata there's going to be some other peripheral information or gaps that you need to fill in so that once everything gets decoded everything aligns properly um and fits into sort of what you can think of as like slots so what i mean by that specifically is when i was getting the data back um before i when i was very close to having the right format but before i introduced these padding pieces in here is i found that there were a lot of gaps showing up in the data so what i was seeing is i would see archive id for example here and then i would see created on be empty and then i would see um in the next element i would see archive id being empty and then created on being filled in but with this instances created on so there was a lot of misalignment happening in the data because technically um the chat message inside of the class in javascript is not the same data member as a struct object inside of rust right and the way that they're allocated in memory is not going to be the same either so we're we are attempting to simulate the data structure as it is presented from rust even though we're working inside of javascript okay so sequence is just another way of saying array so my guess was that since i know the binary data the byte array data is of uh coming in from either coming into or coming out of rust i figured i'd try that as an element of this array it's basically an array and then i would make it two elements long and play with that first i tried like three or four and then i got it down to two and that seemed to fit um you know everything seemed to align so then so then that's the uh the struct itself right this guy itself but again we have an array of these structs coming back so i need to create that array and then i need to give it the same length right and we'll get into what this looks like and then once i do that i have this lo object that i can use to call the decode on the real data and then i i send back and this is this any is coming from the decode because there's no typescript enabled for um for buffer layout so that's why i keep saying that it's any but it is type array of chat messages so then this guy right here is just length 20 because arbitrarily for testing purposes we decided we're only going to use this much data right obviously it would be different for your records your schema whatever that may be alright so so the same thing for this guy but for the other account instead then we take this data and we clean it up and then we turn it into a different type so that our views can digest them more easily um what is going on here so uh this took me a really long time to figure out because like i said there's no documentation for this um alignment business but uh what i found is uh there were all sorts of extraneous um values like this unicode character coming in um you see right here all of this stuff sometimes i was getting plus signs stuff like that as i was playing with it and trying different things so this was an attempt to clean up the actual member values and then after that um i was getting some records that were um just uh bizarre and just that i never inserted that we're just you know showing up usually on the first index so i'm trying to clean that up here and filtering out for those kinds of records this just says you know if the values are all zero then i don't want that particular record so then once i accomplish that like i said before we're not going to go to solana to get the actual data we're going to go to rweave so i created an r weave service right again all the r weave stuff is in the rv folder and it's called get data but before we go through this let's go through some of the initialization in here so first of all i created this uh let's see here yeah so i created this class to represent the container for for our data so this is the actual message that i'm sending and then this is that string of of milliseconds probably in this case we don't need this because all the transactions do have a timestamp but just to keep things consistent and easy i just put it in there so i decided to contain all the related are weave code in this single service class and the way that initialization works is we need to call the r weave um class instance and this is the regular sdk not the test sdk those are two different things so when we initialize in a similar way to solana we need to indicate which network we're pointing to some other important information like the port number of our url and the protocol that we're going to be on so http as opposed to say for example https so then on top of that because we're in a test environment we need to initialize the test weave which will give us our testing wallet and our testing wallet key effectively okay and then i'll i'll explain the save once we get there but for now let's explain the how to get data so this thing is going to take our decoded array of chat messages that are coming in from solana again each of these is going to have the archive id which is a transaction id within our weave so we're going to loop through these chat messages we're going to get each individual message and then we're going to call this r weave transaction get data call and we're going to call it with the archive id okay and then we're going to get back the message we're going to decode it and we're going to claim that it's a string because that's how i entered it into so we're going to get back a string and i i have to manually convert it to a string because it's possible to be a u and 8 array as well so then once i get it back i set this r weave data object with the appropriate fields and then i keep looping the next one the next one until i'm done so once i get back the data in the r weave data format i now have to convert it into this format so i can send it into my views so that they can digest it right so i map it i convert it and then i return it so once i do that right here this is an asynchronous call so i use my callback and then i set it to these one one of these two guys so remember this is the the prop type that i'm going to use to send into my view so just to show you message view is this section right here so that's what i'm passing it down to and in this case i'm just merging them together because i'm going to filter and sort and stuff in here okay so in here this is my actual messages view this guy right here i don't know why this isn't working but this is the actual view it takes in the props these are the props right here and then whenever these props change whenever these props change it runs this code that's going to first sort by the created on right after removing these whatever extraneous characters and then it's going to map and convert these into actual into actual individual chat elements right so for every sorted chat message there's going to be one panel element per so the ones that this person sent show up here and the the ones that this person received show up here and then on the other screen it would be flipped around so that's what this guy here is doing so then if we look at this view inside the views we can see that it receives props that are basically the same but also indicate whether it was sent or received so if sent is false then that means it was received so inside of here what am i doing so i am cleaning up um again some of the uh created on values right i was while i was testing and stuff i was having um some issues so it was just quicker to um eliminate erroneous values that that i had accidentally inserted into the type so that's what this is about you don't unless you're making mistakes you don't need to have this in your code and then here i am converting the milliseconds into an actual number type and then converting that into a javascript date using this format so you can also see the timestamp as well as the date and then i want to indicate stylistically whether this is a received or a sent message so i have styles that if it's a sent message then it'll push to the right hand side if it's a receive message it'll pull to the left hand side so that figure this little mini logic figures that out and then you have the view so this is the call that we saw before and i figured it made sense to put it inside of this view since the output is specific to this view okay so then what's the other main piece um [Music] the other main piece is sending of messages so that's represented by this ui so when after the message is typed and then the person click submit what's going to happen once they click submit they're going to save the actual message just a shrink right this is the real message that appears down here so they're going to save it assuming this save into r weave was successful they get a tx id and then a transaction id from our weave and then that transaction id is going to be saved into solana so it can be a shortcut pointer back to our weave right and in previous videos i showed you how to do this already so let's just look at how to save into our weave so into our weave we're taking our message we are creating a transaction with this call right here so a transaction is basically um or the data of a transaction is basically anything so in that sense it's sort of it's a lot like the data type inside of um inside of an account in solana except you can deliberately say that this is text um or in my case uh yeah text or you can say it's json or whatever by adding this tag but it can also be binary data so i take that i identify with this content type just like in web development and then just like in solana i have my current wallet um sign it which is my test wallet right because somebody has to pay for the transactions and then once it's signed then i can attempt a post which is to save the transactions inside of our weave and then this is only for testing purposes we want to force a mining a mine operation to happen meaning we want the block to be mined and inserted into the chain validly if this was uh happening on the regular um the production main net for our weave there is no such call we simply have to wait um for the block times to finish and this transaction to be posted right so you could put in um a timer or whatever you want in here to have a delay and then once that's done we want to get the status of it because we want to um confirm that you know it was successful so another way to handle this in production might be to to put it into a loop although i would caution against making it an infinite loop i would i would probably say do a loop with a maximum number of retries so then this guy will come back and then let's say that um it takes a few seconds so the first few tries the status is um not 200 because all these statuses are the same statuses as in web calls like 400 200 403 stuff like that so you want to check what the status is and then when the status is equal to 200 then you can exit and return the id of the transaction right and as i said before that transaction id gets saved into solana the account um by the smart contract that we created and solana smart contracts don't have state so you're not saving anything into the smart contract you're saving it into the account that the smart contract has access to so in here okay we're just calling the get messages internal but with this rapper and i was sharing it with this guy all right so let's see what else we could take a look at so we don't need to look at the wallet because we covered most of that in the other videos let's see in here again super important once you compile and then deploy your smart contract into whichever cluster you're running you need to come in here and update the um program id into here otherwise you'll be pointing to my smart contract which which obviously won't work so here okay this is the old code from our previous video i'm not going to bother with that this is the code we just covered before about creating a new chatting account we covered most of this code already uh i think i covered this but if i didn't just to drive the point home these guys are here because when you send the message to solana the tx id we need to make sure that the archive id's actual value and the created on actual value has a length that is correct for the sizes that we selected in our dummy values right so the length must be the same as these which is 16 for created on 43 for the r weave transaction id and then if for some reason they're shorter than then they need to um be filled in with extra zeros that's my placeholder dummy values you can use underscore pipe whatever you want but that's what i chose and then so these functions are attempting to recognize a shortness of the length and then filling in zeros as appropriate and then returning that fully filled in value so that we won't have any issues deserializing or serializing so that's what that's about and then we create our solana instruction we take that object that new single message object we serialize it we stick it in a buffer a blob and we send it ultimately to solana and then similar to our weave we're basically checking how that went and then returning the result so really that's about it um so as you saw these um serialization deserialization this business is definitely not trivial and i suspect that um if you have more sophisticated complicated schema right other types of values you're going to run into your own issues if you really find that you're stuck with your particular data type i would consider converting it to a string like i did um to make things a little bit easier because since you have total control of this code you're able to recognize what the data type should be and then upon the deserialization you can you know hydrate back into the proper format that you need to work in a type safe way so i wouldn't spend too much time trying to wrestle with the serializers because at the end of the day they're not really the main um the main work of your app and of your program it's all the other stuff around it right so if you end up getting stuck like that my recommendation is is to either go with strings or maybe even with um json strings if you prefer and just deal with that instead um what else can i say yeah i think that's it so as usual if you have issues uh leave a question on the comments if you have um better ways of doing you know either some of this for the decoding or some of the other serialization definitely let us know in the comments so that the other guys can benefit from your knowledge and again um my next series of videos is probably going to be coming out on the solana channel so be on the lookout for that and thanks for watching i'll see you next time bye
Info
Channel: David Choi
Views: 12,871
Rating: undefined out of 5
Keywords: solana, arweave, rustlang, javascript, smartcontract, typescript, dapp
Id: Jz5v_u75xk8
Channel Id: undefined
Length: 84min 54sec (5094 seconds)
Published: Sat May 29 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.