In the last lesson, we managed to get our minted NFT to show up on our frontend website. 1
That NFT however was minted through the command line. 2
So we basically initialized a new canister and passing in a bunch of inputs that resulted in this NFT. 3
Now we want to be able to let our users mint their own images, to upload images, and then to create 4
these canisters programmatically and to be able to mint them on the fly. 5
So that's what we're going to be tackling in this lesson. 6
The first thing is we're going to be working on this Minter.jsx. 7
So at the moment it's just got some playing HTML and we want to be able to show that through our app 8
jsx. 9
So I'm going to go ahead and comment out my item here. 10
And instead in between the header and footer, I'm going to add the Minter. 11
So again, selecting it from the dropdown, it will auto import it at the top 12
and then we're going to set this as a self-closing tag as with all our React components. And let's hit 13
save and just see what it looks like. 14
This is a very simple form with basically just two fields. 15
We've got an upload image where we can click on this button, 16
it'll bring up our finder and you'll be able to choose an image to upload. 17
Now we've also got the section where we can add our collection name and collection number and whatever 18
it is you want to show up as the title of the NFT. 19
And then we've got this little button here, which once we click it, should trigger all of the background 20
stuff and get our NFT minted and our cannister created. 21
So this is what it looks like right now. 22
The first thing we want to do is to be able to pull in all of the information that's entered into this 23
form right here. 24
You'll notice that we've got our two inputs here; our input for uploading and our input for the name. 25
Now the name is a lot easier, so let's work on that first. 26
Whereas normally we would use our value and our on change attributes here in order to safely keep track 27
of what's entered into the input, 28
in this case, we're actually going to use something a little bit different, and we're going to use 29
something called useForm, which comes from a module that we installed called the React Hook Form. 30
And this is a really nice way of pulling in data that's been uploaded or entered into a form. To begin, 31
let's go ahead and register the form, and we do it in a very similar way to how we would use our use 32
state. 33
So we create a new constant, but instead of opening up square brackets, we're going to open up curly 34
braces. 35
The first component is going to be called register, and this is going to be an object that we can tap 36
into in order to register and add in all of the inputs that the user creates on our form. 37
And then the second input is the action, right? 38
So this one is going to be the handleSubmit. 39
Basically, it's going to be the function that's going to be called when the submit button gets triggered. 40
And we're going to set it equal to useForm. 41
Now, useForm is not one of the native React hooks, so we actually have to import it. 42
So we're going to say import and then we're going to open up a set of curly braces, useForm and we're going 43
to import it from react-hook 44
-form. 45
So now we can use form and we're going to initialize it to empty. 46
So now comes to the stage where we actually register our inputs. 47
Right over here where we have our inputs, 48
we can add another line by hitting enter and we're going to add to the objects that are already registered. 49
So we're going to use that triple dot notation that you've seen before in JavaScript lessons. 50
And I'm going to add to that register object, and then I'm going to give it a name so that we can identify 51
it later on. 52
And this I'm going to call the name. 53
And then after that, we can add some options and I'm going to add a required option and I'm going to 54
set that to true. This way 55
it makes sure that users will actually enter something into here instead of leaving it blank. 56
So now that we've registered this name, we've got to trigger the handle 57
Submit at some point in our code. And in our code, we've got this Mint MFT button which is actually formed 58
by a span. 59
We can add an onClick to this span in order to make it trigger our handleSubmit. 60
And let me just make sure that I've actually spelt this correctly. 61
And then inside this method, we pass in the name of a method that we've got that will actually handle 62
the submitting. 63
So I'm going to create a new asynchronous function here, which I'm going to call onSubmit. 64
And this is going to be passed the data from our form. 65
So we can now tell our handleSubmit that that's the method that you should call and pass in all of 66
the components that were registered into the form data. In terms of the flow of data, 67
first, the user types something into this input which gets registered as an object into all of the data. 68
And then once this button is clicked, then it's going to trigger the handleSubmit, which comes from 69
our useForm hook and it's going to trigger the onSubmit button, which we've defined here passing 70
in the data. 71
So now in order to make sure that this actually works, let's go ahead and console.log our data. 72
So we're going to say data. and then we're going to give the name that we registered here, which 73
is name, data.name. 74
And let's go ahead and see what that looks like. 75
Let's hit save and go over here, 76
type something. 77
I'll just type hello and then click Mint NFT and you can see what I typed show up down here in the 78
console log. 79
So now that we've made it work for the text input where we enter the name of our collection, 80
let's go ahead and make it work for our upload input as well. 81
So we do basically exactly the same thing. We add to the end of our object through using register, and 82
we give it a name that we'll be able to use it to identify with, 83
and I'm just going to call it image. And I'm going to set the required property to true as well. 84
Both of these will be obligatory inputs. 85
Again handleSubmit will call onSubmit passing in all the data, and if I go ahead and console.log the 86
data.image and hit save and go over to here. 87
And if you take a look at the current Udemy lesson, you'll find a download to a collection of cryptodunks 88
that I've created for you. 89
They're absolutely tiny because they are 20 pixel by 20 pixel large, but we're going to enlarge them 90
on our frontend so you don't have to worry about that. 91
So once you've downloaded and extracted the folder, you'll be able to open it and just choose any one 92
of these to upload. 93
And it doesn't really matter what name I'm going to give it because I'm only logging the image. 94
Now, if I click on the button and submit those inputs, then you can see that what gets printed here 95
when I print data.image is a file list because you can upload multiple files, right? 96
So the first item in our file, the zeroth item, is our image cryptodunk3.png. 97
So that works perfectly. 98
Now let's go ahead and delete our console logs and actually use this data to mint our NFT. 99
First, I'm going to create a new const called name and I'm going to set it to the data.name, which 100
is what I used to register the contents of that input. 101
And then I'm going to create a new const called image, which is going to be set to data.image. 102
But remember just now we saw that it was an array. 103
We have to get hold of the first item in that array. 104
So we're going to add the [0]. Because this data image contains a whole bunch of data 105
that we don't actually want to store on the blockchain, including the file name and file size and all 106
of that metadata, 107
we're going to strip it down into just the bare bones, actual byte data so that we can store that data 108
on the blockchain and be able to pull it out and display it. 109
Let's go ahead and create a new a constant, which I'm going to call the imageByteData. 110
And I'm going to create an array which is going to be an array of Nat8 numbers or something that 111
can be converted into Nat8 numbers that we can put in here eventually. 112
In order to do that, we're going to use the three dot notation to add to an empty array. 113
And inside here I'm going to create a new Uint8array and I'm going to initialize it by calling 114
await and I'm going to use the image.arrayBuffer. 115
And we end that method call with a set of parentheses. 116
The ArrayBuffer object can be used to represent generic binary data, and we're going to store the 117
binary data for our image that's uploaded. 118
We can create it either from scratch or tap into the blob.arrayBuffer, and as it says, it returns 119
a Promise. 120
So that's why we need to use the await in front of this. 121
And it basically turns the contents of our blob as binary data into an array buffer containing our binary 122
data. 123
So once we've done that, making sure that there's no typos in that line of code because it's a bit 124
complex with all of these things, if you want, you can actually split this off into a separate line 125
if it's easier for you to see. 126
So you can say image.arrayBuffer and store that in a const which we can set as the image array. 127
And then we can turn that image array into a Uint array and then store it inside this constant which will 128
be able to match this Nat8 array data type. 129
So now all that's left to do is to tap into our backend and actually mint this NFT. 130
You might think that's done through the NFT actor class, but actually we're going to write the code 131
in our main.mo file because this is our main backend. 132
This is going to store these NFTs as a HashMap. 133
We're going to go via this to programmatically create new canisters and do something similar to what 134
we did in the command line, but from our backend, 135
so backend-to-backend kind of interaction. 136
Inside this canister in our main.mo, 137
we're going to create a new function, and it's going to be a public shared function 138
passing in the message so that we can tap into the msg.caller later on. 139
And then this function is going to be called mint, and it's going to need the image data in the format 140
of an array of Nat8 numbers. 141
It's really important that you don't just leave it as Nat. It has to be Nat8 for all our files to be 142
compatible. And then it's also going to take the name of the NFT as well, which is going to come in 143
in the format of text. 144
And then once this minting is all done, we're going to asynchronously actually return an output and 145
the output is going to be the principal of the newly minted canister. 146
We've got some errors here because we're not returning anything yet. 147
And also in order to use this principal type, we have to import it. 148
So let's go ahead and add that to the top here and import this principal type. 149
And now let's actually write the logic for this. 150
Firstly, let's create the owner of the NFT. 151
So we're going to say let owner, 152
and this is going to need to be a principal data type, but we're going to set equal to the msg. 153
caller. 154
So this is why we made this a shared function, so that we can access the identity of the user who called 155
this method. 156
Now, because it's going to be called from the frontend over here when that onSubmit gets triggered, 157
then that means, if you remember from our previous lessons on authentication, if we authenticate the 158
user and log in the user on the frontend, then when we trigger this, it will pass in their principal 159
ID. 160
So that is going to be set as the owner of the NFT. 161
Now we're not going to deal with the authentication because we've covered that extensively before. 162
So this is just going to come in as the anonymous user who has a defined principal which we're going 163
to set as the owner of the NFT. 164
Now, the next stage is the programmatic creation of our NFT canister. 165
And we're able to do that because we've created it as an actor class so we can initialize it and then pass 166
in all of the data that it needs in order to hold onto this as a unique token with a unique identifier 167
and all of the image data. 168
In order to initialize that, we have to first import that file. 169
I'm going to say import and then I'm going to give that file a name, which I'll call the NFTActor 170
Class. 171
And we're going to be able to find it by going one step up, 172
so ../, and then going into the NFT folder and then /nft. 173
So now all the errors should disappear because it can actually see that, nft. 174
mo, and we can use it here to initialize it. 175
I'm going to create a new constant called newNFT and I'm going to set it equal to the NFTActorClass 176
. 177
and then from that actor class, I'm going to initialize a new NFT which requires some inputs to be 178
able to be initialized. 179
So at the moment, if you take a look at this, we need to supply the name, the owner and the content, 180
and I'm going to do it in that order. 181
So the first thing I'm going to supply is the name that gets passed in to this method. 182
Next is the owner, 183
so I'm going to pass in this principle. 184
And finally, it's the content which is again passed in to this method as the imgData. 185
The initialization is going to be asynchronous. 186
So that's why this is telling you that it's going to return asynchronously. 187
As with all of the async method calls, we have to add the keyword await. 188
Now we've managed to get rid of that error, but we've still got an error here because we haven't yet 189
returned even though we promised to. 190
So let's just silence that error for now by actually getting hold of the principal ID of this newly 191
created canister. 192
Let's create a new constant, which I'm going to call the newNFTPrincipal. 193
And this is going to come from the newly created canister. 194
But at the moment, there's no easy way of getting hold of the canister ID of a newly minted NFT. 195
So the easiest way to do this is to bind the this keyword to this actor class. 196
So this now then represents this entire actor class. 197
And we can create a public query function which is going to allow us to get hold of the canister ID. 198
So I'm going to call it getCanisterID and return asynchronously a principal. 199
And what we're going to return is going to be the principal of this actor class. 200
And we can get hold of it through the principal.fromActor method, which is going to take an actor 201
and give you back the principal of that actor. 202
And normally, if this was just an actor, not an actor class, we can actually simply just pass in 203
the name of the actor. 204
But because this expects all of these inputs, by binding it to this, we can simply pass in this actor 205
right here. 206
Now, back over here, 207
we can call await 208
newNFT, which is the newly minted one, and we can call getCanisteID to get it back. And we can return 209
that as the output. 210
So now all our errors should be gone, 211
but there's one thing that we still have to think about which is a more sort of practical component. 212
If you remember, every single time we created a new canister on the actual live Internet computer network, 213
we had to allocate it some cycles. And when we create canisters programmatically like we're doing here, 214
we have to do the same thing. 215
So if we do this on our localhost, our local version of dfx, it won't really mind because it's not going 216
to care because it doesn't actually do the accounting. 217
But once we deploy this live onto the Internet computer, we will need to make sure that it gets some 218
cycles in order for it to actually be created on the live network. 219
The way that we can add cycles currently, at least in 2022, is through something called the Experimental 220
Cycles module. 221
And the way that we do this is simply by writing Cycles.add, 222
and hopefully if you manage to select the add method and hit enter, it will automatically import the 223
cycles from the Experimental Cycles module. 224
Now the reason why it's experimental is because the team and the people involved in governance for Dfinity 225
haven't actually yet decided how exactly they're going to implement this cycles module in the future. 226
So right now, this is the only way to do it, and it's pretty simple. 227
All you do is you say cycles.add, and then you specify how many cycles you want. 228
Now it costs 100 billion cycles to create a new canister. 229
And in addition, we're going to give it 500 million cycles in order to keep it up and running. 230
So the final number will look something like this. 231
And once we've added it, then those cycles will come from this main canister and be allocated for the 232
next canister that gets created. 233
We can check this by actually using a debug. 234
So let's go ahead and import the debug module. 235
And use the debug_show to show how many cycles we had before, 236
so Cycles.balance. 237
And let's copy and paste this to add it on the line after we've created the canister. 238
Now that we've created our method and completed it in the main.mo, let's go ahead and call it 239
and pass on all of the bits and bobs that we've collected from the users inputs. 240
In order to call the methods from our main, we have to import it. 241
So I'm going to import my canister, which is called 242
opend, which is specified in the dfx.json. 243
And then I can add in its location. 244
So it's going to be ../../../ and 245
we should see declarations. 246
And then I'm going to select the opend folder like this. 247
And below all of these lines, inside the onSubmit 248
I'm going to create a const which is going to hold the new and newNFTID and it's going to come from me 249
calling the await and then the opend. and then the name of our method here, which is called 250
mint passing in these two pieces of information, imgData and the name. 251
So opend.mint 252
and then the first thing is the image. 253
So I'm going to pass in the imageByteDaata and the second is the name. 254
Now all of our constants are active and all that's left is to go ahead and console.log our newNFTID. 255
And because it's going to come back as a principal, I'm going to use the toText, which comes from 256
the principal type. 257
So let's go ahead and import that as well. 258
And now hopefully, if everything works, I should be able to view the cycles being printed over here. 259
And also once all of that's done and successful, on my frontend on my console, I should see the new 260
new NFT's principal ID being printed as well. 261
So let's hit save and let's go ahead and deploy all our changes to our canisters and our front end 262
by going to our read me and copying that command line command. 263
So let's go ahead and paste it, and this is going to hopefully update all of our code and make sure 264
that actually works. 265
So once that's done, let's head over to our front end, 266
choose one of the pictures from our crypto dunks, give it a name, give it a serial number if you want, 267
and then click mint. 268
And if we take a look over here, you can see that the first print is how many cycles we had in our 269
balance for our main canister, 270
and the second print is what's happened to the balance after we've allocated it to this new canister 271
and you can see it reduced. 272
And on the front end we've got the canister ID of the new NFT being printed, which means we can actually 273
use that to display the NFT. Because once this is done, we want to be able to show it right? 274
Otherwise, how would the user know? 275
That means we can tap into our item.jsx and to render it once we're done with creation. 276
So here we can say if the NFT principal, (don't worry we don't have this yet, we're going to create 277
it a little bit later on) is equal to an empty string, 278
well, then we're going to return all of this, 279
so going all the way down to the very last closing parentheses and add in our closing brace. 280
But otherwise, namely, if it's not an empty string and we've actually given it some value, then we're 281
going to render something else. 282
And the something else is going to come from the read me. 283
So if you scroll down in the read me, you can look at it through preview. 284
I've got the minter else HTML which is basically all the HTML that's going to go into the else statement. 285
And I want you to just simply copy and paste it in because it's got a whole bunch of class names. 286
I don't want you to type it all out. 287
Now, what we've got here is it's going to change the form from what it was previously into one that 288
just has an H3 with a title that says Minted!, 289
so to tell them that it was successful. And then inside this second div, which says horizontal-center, 290
we're going to render our item. Again selecting the item from the dropdown, hit enter so that it gets 291
imported, 292
and then we're going to close it off as a self-closing tag. 293
And remember that this item that we created here takes an id as a prop which accepts a text principle, 294
which, if you remember, we already have printed in here. 295
So now all we need to do is to use our React useState hook to create that constant that we specified 296
here which is the nftPrincipal, and the setNFTPrincipal and useState and set it to an empty string 297
to begin with. 298
So when our component Minter first loads up, the state of this is going to be an empty string, which 299
means it's going to render our form. 300
But as soon as we've managed to get hold of the ID by calling this minting function, then we can set 301
our NFT principle to the new NFT ID. And we can also use that to pass in as the prop. And we're going 302
to set the ID to the NFT principal. 303
But remember, this has a principal format, so we have to turn it to a text format in order to match 304
with what's expected over here, 305
so similar to what we had previously in our app component. 306
A couple of things are important. 307
Make sure that you spelt the prop as id and make sure that you're passing in the nftPrincipal which 308
is set over here, 309
so the nftPrincipal.toText. Now let's hit save and let's try it out. 310
So here's our frontend. 311
Let's choose one of the files. 312
Give it a name. 313
Click 314
Mint. 315
And it tells us that nothing was returned from render, which says a return statement is missing. 316
So let's check our code, 317
and indeed, in our if statement we've got our return 318
but in our else, I've actually forgotten this. 319
So there are a lot of potential mistakes even when we try to avoid them. 320
Sometimes they happen, but it's good to know and see it so that you know what's going on. 321
So let's try that again. 322
And you can see now we've got our minted title, 323
we've got the new NFT showing up, 324
we've got its name and the owner all working correctly. 325
The only thing that's left is to give the user an indication when that NFT is being minted. 326
So ideally, we'd really want a loader in here when that minting process gets triggered. 327
And again, if you head over to the readme and scroll down, you should see the loader HTML, which 328
you can copy wholesale and we're going to be pasting it into our Minter. And I want it to show up just 329
above our H3 title here. 330
So inside the div that's got the class name minter-container and above the H3, we're going to paste in 331
our div. Now we're going to give this a hidden attribute and I'm going to set that to be called LoaderHidden, 332
which we're going to useState to keep track of. 333
And the setLoaderHidden 334
to update it's state. Now we're going to start the LoaderHidden with true so that it doesn't show up, and 335
then as soon as our form is submitted, so they press that button, we're going to set the loader hidden 336
to be false so the user can see the loader showing up. 337
And then once we've got our newNFTID back, then we're going to set the loader hidden back to true 338
again so that it disappears off the screen. 339
Now let's try this finally, and you can see the full functionality of our minting process. 340
So let's choose a file, 341
give it a name. 342
And as soon as I click on Mint, you can see our loader show up which gives the user an indication 343
that something is happening. 344
You don't have to keep pressing. 345
And finally, we get our minted NFT showing up and with all of the canister details being pulled in 346
and displayed through the item component. 347
So now users can actually mint NFTs on the internet computer using our OpenD website. 348
Now in the next lesson, we're going to figure out how we can display all of these NFTs that we mint 349
in the My NFTs section of this website. 350
So for all of that and more, I'll see you there.