In the previous lesson, we managed to get the balance checking mechanism to work on our frontend and 1
our backend. 2
Now it's time to tackle this full set so that anyone can get some free tokens from the central pool, 3
just like how you claimed the free ICP tokens. 4
To do that, let's create a new public function which we'll call payOut. 5
And this function is not going to take any inputs, but it is going to asynchronously return some piece 6
of text. 7
And by default, it's just going to return the word Success. 8
This will then be able to be used when we call this function on the frontend, and we can replace the 9
words inside the button here with whatever it is that we got returned. 10
So if it was successful to faucet this amount to the user, then will replace this 'gimme gimme' with 11
success. 12
So that's the purpose of that return. 13
Now, how do we get hold of who to send the money to or who to send the tokens to? 14
Well, in Motoko, there is something called a shared function, and this basically allows methods declared 15
inside an actor to be called by other actors. 16
Now, by default, all public methods are shared anyways, but there's something that we can use inside 17
this shared method that's really useful. 18
We can use it to identify the principal ID of the entity that called a particular function. 19
So we can do that by writing the keyword shared and then adding a parameter called msg. 20
And whenever something, some canister or some user calls this method, then their principal ID is going 21
to be embedded inside this msg parameter and it's the caller property. 22
That means we can use this to get hold of the entity that called a particular function. 23
And if we can get hold of that, well, then we can assign that principal ID a particular amount and 24
add them to our ledger in our balances HashMap. 25
Let's go ahead and make this function shared. 26
And then inside the parentheses we add that parameter, which is called msg. 27
Now let's go ahead and use our debug print to print out what is the msg.caller in our case. 28
So we're going to use the Debug.print and then debug_show and 29
I'm going to pass in the msg.caller. And we need to import debug in order to use it. 30
Now let's hit save and let's go ahead and deploy our canister with these updated changes. 31
And in our command line over here, we can call that method payOut by writing dfx canister call and 32
then the canister name is token and then the name of the function is called payOut and notice the way 33
that it's spelt. 34
Make sure that it matches what you've got in your main.mo and then we can go ahead and hit enter. 35
Now, once that's done, you can see here we get the return of success, which doesn't really mean anything 36
because everything will return success right now. 37
But if you take a look at the panel where you've got your dfx start, where you've got the simulated ICP 38
blockchain, you should be able to see the id of the caller. 39
So in this case, the caller ID is my user, right? 40
If you match it up, you'll see that my user principal ID is this and that's what got printed out when 41
I printed the msg.caller. 42
Now let's try something a little bit different. 43
Let's call this message from our frontend. 44
So inside our faucet.jsx, let's go ahead and tap into that function. 45
So let's first import everything that's inside our token bridge. 46
So import token from ../../../ declarations/token. 47
And inside the async function to when the click is handled, we're going to use await token.payOut. 48
And this is going to trigger this function but it's going to trigger it from the frontend. 49
So it's going to be the user that's using the website and then hopefully we should get this debug print 50
triggered down here. 51
Let's go ahead and hit save. 52
And because we just made some changes to the frontend, not the backend, we can simply hit save and 53
it should refresh. 54
And now if I click on Gimme Gimme, then I go back to my command line, 55
you can see that this time there's a different principal ID that got printed. And this is the ID 56
of an anonymous user that's using the frontend website, instead of the actual user ID of myself. 57
So you can see, depending on who called that function, the msg.caller will reflect the principal 58
ID of that user or canister. 59
We can use this fact to transfer that particular ID some tokens. 60
To do that, we can simply tap into our balances HashMap and we can use the put method to put a key 61
and value pair in. 62
The key is going to be whoever it is that called this function, 63
so the principal ID of the msg.caller, and the value is going to be however amount we want to transfer 64
to them. 65
Now, inside my faucet, I've promised people that they can get 10,000 DANG coins, so I often have 66
coin and token mixed it up. 67
So if you want you can change that to token. 68
It doesn't really matter, but the amount I've set is 10,000. 69
Let's go ahead and set that here as well. 70
So we're going to give whoever calls our payOut method from the faucet 10,000 tokens and we're going 71
to do that using balances.put. Because we've got our check balance functionality already set up, 72
we can actually check to see if this works. 73
So if I go ahead and hit save and I deploy my updated code inside the payOut function, then I can go 74
ahead and check what is the balance of my user's principal. So I can paste that in there, check balance, 75
and you can see this has 1 billion DANG tokens, but I can also check the anonymous user who's actually 76
using this website. 77
If I paste that principal ID in here, click check balance, 78
you can see that this account has a balance of zero DANG. But as soon as I click Gimme Gimme, which 79
will faucet 10,000 DANG tokens into this account, afterwards, 80
if I go ahead and click Check Balance, then you can see we successfully transferred 10,000 tokens into 81
this user's account. 82
So we've managed to get the faucet functionality somewhat working. 83
Now there's a couple of user experience things I want to improve here. 84
The first thing I want to change is when I click on this button, it actually takes a little while to 85
perform this method. Because it's an update method 86
instead of a query method, it takes something like 2-3 seconds, and we don't want the user to be pressing 87
this continuously because it's just going to mess up our backend. 88
What we want to do is to be able to press it and then to disable the button until this functionality 89
has completed. 90
To do that we can go into our faucet frontend, 91
so the faucet.jsx, 92
and inside here we can set a new constant which I'll call isDisabled and the setter will be setDisable 93
and this is going to need to useState. 94
So we're going to import that in here as well, which hopefully should happen automatically because 95
you're using VSCode. And the initial state we're going to set it to is going to be false. 96
So we don't want the button to be disabled. 97
So this is the button, we've got the button ID, we've got the onClick, 98
and I'm going to add one more, which is the disabled attribute and I'm going to set that equal to the 99
isDisabled state using the state hook up here. 100
So now as soon as the user clicks the button, we're going to set it to disabled. 101
So we're going to say setDisable to true. 102
And then once the token payout has completed, then we can setDisable back to false again. 103
Alternatively, you can actually just omit this line because well, you know, once they've clicked 104
it once, should they really be allowed to tap faucet many times? So you could actually just keep 105
it disabled. And we can see how this works on the frontend. 106
Now if I click on it, you can see it turns gray and 107
I can't click on it any more. 108
And that means this button will stop working and I can't keep requesting DANG to this account. 109
One last consideration is we've transferred 10,000 tokens to the msg.caller, so the user of the website 110
right now, but what if they go ahead and spend that money and then they come back and they want to 111
get 10,000 tokens again? 112
Well, that shouldn't really be allowed because the idea is that you get 10,000 tokens to play with 113
and you can mess around with the website or do whatever it is you want with these tokens, 114
but it's not like an endless stream, right? 115
Because otherwise you're just giving people free money and it will really affect the token economics 116
of your particular token. 117
So let's add in a check into our main.mo. 118
What we can check for is to see if inside our balances HashMap, does this key actually exist? 119
We can use the get method to get hold of the value that's associated with msg.caller. 120
And the way that the get method works, if you remember, is that if that key doesn't actually exist, 121
it will return null. 122
If it does exist, then it will return the associated value. 123
What we can check inside this if statement is if the balances.get parsing in the current msg. 124
caller is equal to null, then that means that this person has actually never requested any tokens. 125
They don't actually exist in our ledger. 126
In that case, we're going to go ahead and transfer them these 10,000 tokens, 127
their starting tokens to play with. 128
But, 129
otherwise we're going to say, well, actually, you've already claimed the tokens, 130
right? 131
And now what we can do is we can output these two different pieces of text from our method. 132
And that means we can get hold of this result either success or already claimed on our frontend here 133
and we can use that to set the button text right here. 134
If we go ahead and create a new constant called buttonText 135
and we'll have a setText. 136
Then we can use state and set that to initially start with the gimme gimme. And after they've pressed 137
on the button, however, we're going to change this 138
depending on whatever it is that we got back from this method. We can create a new const called result 139
and set it equal to the outcome of this method. 140
And then we can use the setText setter to set the button text to whatever we got back from the result 141
here. And now, because we've updated the backend, let's test this out by first doing dfx deploy and 142
see if this actually works. 143
Remember that at the moment we have no persistence, so our balances HashMap gets overwritten every 144
single time we do dfx deploy. 145
That means if I take this user's principle id, so this frontend user's principal ID, and I check the balance, 146
it should have zero 147
DANG. 148
If I go ahead and click Gimme Gimme, then it should return with the result once it's done. 149
So it says it was successful. 150
And now if I check the balance again, it should now have 10,000 151
DANG. 152
But if I try to click that button again, which I actually can't because it's disabled, but notice 153
that whenever you have a button that's disabled, if the user just refreshes the website, the state 154
will get reset. 155
But now if we check the faucet again, it actually tells us, wait a minute, your user ID already 156
exists on our ledger. 157
So that means you've already claimed your tokens and you shouldn't be allowed to claim it again. 158
So we've now managed to create our faucet by giving 10,000 of our custom tokens to anyone using our 159
website. 160
And we've also added in some security and some user experience touches to make this work properly. 161
In the next lesson, we're going to tackle the last panel here, which is the transfer. 162
The idea is once you've claimed your 10,000 tokens, well you can actually then transfer that from your 163
account to any other account. 164
Let's say that you didn't actually need your 10,000 and you wanted to transfer it to a friend who needed 165
2000 DANG tokens, or you could do that using this transfer area. 166
So for all of that and more, I'll see you on the next lesson.