This repository has been archived by the owner on Mar 11, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 160
/
tutorial.html
1030 lines (921 loc) · 58.9 KB
/
tutorial.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!DOCTYPE html>
<html>
<head>
<!-- Google Tag Manager -->
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-WHT4PS2');</script>
<!-- End Google Tag Manager -->
<!-- HubSpot Forms -->
<!--[if lte IE 8]>
<script charset="utf-8" type="text/javascript" src="//js.hsforms.net/forms/v2-legacy.js"></script>
<![endif]-->
<script charset="utf-8" type="text/javascript" src="//js.hsforms.net/forms/v2.js"></script> <!-- Start of HubSpot Embed Code -->
<script type="text/javascript" id="hs-script-loader" async defer src="//js.hs-scripts.com/4795067.js"></script>
<!-- End of HubSpot Embed Code -->
<meta charset="utf-8">
<meta name="description" content="This tutorial will take you through the process of building your first dapp---an adoption tracking system for a pet shop! This tutorial is meant for those with a basic knowledge of Ethereum and smart contracts, who have some knowledge of HTML and JavaScript, but who are new to dapps.">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="environment" content="">
<!-- Favicon -->
<link rel="icon" href="/img/favicons/favicon.ico">
<!-- Add to homescreen for Chrome on Android -->
<meta name="mobile-web-app-capable" content="yes">
<meta name="application-name" content="Truffle Suite">
<link rel="icon" sizes="192x192" href="/img/favicons/chrome-touch-icon-192x192.png">
<!-- Add to homescreen for Safari on iOS -->
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="Truffle Suite">
<link rel="apple-touch-icon" href="/img/favicons/apple-touch-icon.png">
<!-- Tile icon for Win8/10 (144x144 + tile color) -->
<meta name="msapplication-TileImage" content="img/favicons/ms-touch-icon-144x144-precomposed.png">
<meta name="msapplication-TileColor" content="#3372DF">
<!-- Facebook OpenGraph -->
<meta property="og:site_name" content="Truffle Suite" />
<meta property="og:description" content="This tutorial will take you through the process of building your first dapp---an adoption tracking system for a pet shop! This tutorial is meant for those with a basic knowledge of Ethereum and smart contracts, who have some knowledge of HTML and JavaScript, but who are new to dapps." />
<meta property="og:title" content="Tutorial | Pet Shop | Truffle Suite" />
<meta property="og:url" content="https://trufflesuite.com/tutorial" />
<meta property="og:type" content="article" />
<meta property="og:image" content="https://truffleframework.com/img/favicons/truffle-share.png" />
<!-- Twitter Card -->
<meta name="twitter:card" content="summary" />
<meta name="twitter:site" content="Truffle Suite" />
<meta name="twitter:title" content="Tutorial | Pet Shop | Truffle Suite" />
<meta name="twitter:description" content="This tutorial will take you through the process of building your first dapp---an adoption tracking system for a pet shop! This tutorial is meant for those with a basic knowledge of Ethereum and smart contracts, who have some knowledge of HTML and JavaScript, but who are new to dapps." />
<meta name="twitter:creator" content="Truffle Suite" />
<meta name="twitter:domain" content="trufflesuite.com" />
<meta name="twitter:image:src" content="https://truffleframework.com/img/favicons/truffle-share.png" />
<meta name="google-site-verification" content="BSgPkMHzw7IxJTpEElNfD8ZZYPzXgOQiTVPzAxAG8-o" />
<title>Tutorial | Pet Shop | Truffle Suite</title>
<link href="https://fonts.googleapis.com/css?family=Grand+Hotel|Open+Sans|Oswald|Varela+Round|Roboto+Condensed|Roboto+Mono|Cinzel" rel="stylesheet">
<link rel="stylesheet" href="https://use.typekit.net/okj1vue.css">
<script src="https://kit.fontawesome.com/371f2d7a8a.js" crossorigin="anonymous"></script>
<link rel="stylesheet" href="/css/index.css?cache_buster=1623735132864">
<!-- start Mixpanel --><!--<script type="text/javascript">(function(c,a){if(!a.__SV){var b=window;try{var d,m,j,k=b.location,f=k.hash;d=function(a,b){return(m=a.match(RegExp(b+"=([^&]*)")))?m[1]:null};f&&d(f,"state")&&(j=JSON.parse(decodeURIComponent(d(f,"state"))),"mpeditor"===j.action&&(b.sessionStorage.setItem("_mpcehash",f),history.replaceState(j.desiredHash||"",c.title,k.pathname+k.search)))}catch(n){}var l,h;window.mixpanel=a;a._i=[];a.init=function(b,d,g){function c(b,i){var a=i.split(".");2==a.length&&(b=b[a[0]],i=a[1]);b[i]=function(){b.push([i].concat(Array.prototype.slice.call(arguments,
0)))}}var e=a;"undefined"!==typeof g?e=a[g]=[]:g="mixpanel";e.people=e.people||[];e.toString=function(b){var a="mixpanel";"mixpanel"!==g&&(a+="."+g);b||(a+=" (stub)");return a};e.people.toString=function(){return e.toString(1)+".people (stub)"};l="disable time_event track track_pageview track_links track_forms track_with_groups add_group set_group remove_group register register_once alias unregister identify name_tag set_config reset opt_in_tracking opt_out_tracking has_opted_in_tracking has_opted_out_tracking clear_opt_in_out_tracking people.set people.set_once people.unset people.increment people.append people.union people.track_charge people.clear_charges people.delete_user people.remove".split(" ");
for(h=0;h<l.length;h++)c(e,l[h]);var f="set set_once union unset remove delete".split(" ");e.get_group=function(){function a(c){b[c]=function(){call2_args=arguments;call2=[c].concat(Array.prototype.slice.call(call2_args,0));e.push([d,call2])}}for(var b={},d=["get_group"].concat(Array.prototype.slice.call(arguments,0)),c=0;c<f.length;c++)a(f[c]);return b};a._i.push([b,d,g])};a.__SV=1.2;b=c.createElement("script");b.type="text/javascript";b.async=!0;b.src="undefined"!==typeof MIXPANEL_CUSTOM_LIB_URL?
MIXPANEL_CUSTOM_LIB_URL:"file:"===c.location.protocol&&"//cdn4.mxpnl.com/libs/mixpanel-2-latest.min.js".match(/^\/\//)?"https://cdn4.mxpnl.com/libs/mixpanel-2-latest.min.js":"//cdn.mxpnl.com/libs/mixpanel-2-latest.min.js";d=c.getElementsByTagName("script")[0];d.parentNode.insertBefore(b,d)}})(document,window.mixpanel||[]);
let devPort = '9000';
let devToken = 'ef061cba76a9bbc41219d6382fc12f23';
let prodToken = 'Production Token';
//If the hostname is anything other than your production domain, initialize the Mixpanel library with your Development Token
if (window.location.port.search(devPort) < 0) {
mixpanel.init(prodToken);
} else {
mixpanel.init(devToken);
}
</script>--><!-- end Mixpanel --></head>
<body class="light">
<!-- Google Tag Manager (noscript) -->
<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-WHT4PS2"
height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
<!-- End Google Tag Manager (noscript) -->
<nav class="navbar navbar-expand-md navbar-light fixed-top" id="primaryNav">
<a class="navbar-brand current" href="/">
<img class="suite-logo" src="/img/truffle-logomark.svg" alt="Truffle Logo" /><span class="d-none d-lg-block">TRUFFLE SUITE</span>
</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarsExampleDefault" aria-controls="navbarsExampleDefault" aria-expanded="false" aria-label="Toggle navigation">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<div class="collapse navbar-collapse" id="navbarsExampleDefault">
<ul class="navbar-nav ml-auto">
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" id="suiteDropdown" href="#" data-toggle="dropdown">SUITE</a>
<div class="dropdown-menu">
<a class="text-truffle" href="/teams">
<img class="suite-logo" src="/img/tt-logomark.svg" alt="Truffle Teams Logo" /><span class="narrow">T</span>RUFFLE TEAMS
</a>
<a class="text-truffle" href="/truffle">
<img class="suite-logo" src="/img/truffle-logomark.svg" alt="Truffle Logo" /><span class="narrow">T</span>RUFFLE
</a>
<a class="text-ganache" href="/ganache">
<img class="suite-logo" src="/img/ganache-logomark.svg" alt="Ganache Logo" />Ganache
</a>
<a class="text-drizzle" href="/drizzle">
<img class="suite-logo" src="/img/drizzle-logomark.svg" alt="Drizzle Logo" />dri<span class="drizzle-z-skew-1">z</span><span class="drizzle-z-skew-2">z</span>le
</a>
</div>
</li>
<li class="nav-item">
<a class="nav-link" href="/docs">DOCS</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/guides">GUIDES</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/tutorial">TUTORIAL</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/boxes">BOXES</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/blog">BLOG</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/community">COMMUNITY</a>
</li>
</ul>
</div>
</nav>
<main class="banner banner-light-cream pt-5 pb-5" role="main">
<div class="container container-post">
<div class="row justify-content-center">
<div class="col">
<p>
<img class="img-fluid" src="/img/tutorials/pet-shop/petshop.png" title="Ethereum Pet Shop" alt="Ethereum Pet Shop" />
</p>
<p>This tutorial will take you through the process of building your first dapp---an adoption tracking system for a pet shop!</p>
<p>This tutorial is meant for those with a basic knowledge of Ethereum and smart contracts, who have some knowledge of HTML and JavaScript, but who are new to dapps.</p>
<p class="alert alert-info">
<strong>Note</strong>: For Ethereum basics, please read the Truffle <a href="/guides/ethereum-overview">Ethereum Overview</a> tutorial before proceeding.
</p>
<p>In this tutorial we will be covering:</p>
<ol>
<li>Setting up the development environment</li>
<li>Creating a Truffle project using a Truffle Box</li>
<li>Writing the smart contract</li>
<li>Compiling and migrating the smart contract</li>
<li>Testing the smart contract</li>
<li>Creating a user interface to interact with the smart contract</li>
<li>Interacting with the dapp in a browser</li>
</ol>
<h2 class="link-markdown">
<a class="anchor-target" name="background"></a>
<a href="#background"><i class="fas fa-link"></i></a>
Background
</h2>
<p>Pete Scandlon of Pete's Pet Shop is interested in using Ethereum as an efficient way to handle their pet adoptions. The store has space for 16 pets at a given time, and they already have a database of pets. As an initial proof of concept, <strong>Pete wants to see a dapp which associates an Ethereum address with a pet to be adopted.</strong></p>
<p>The website structure and styling will be supplied. <strong>Our job is to write the smart contract and front-end logic for its usage.</strong></p>
<h2 class="link-markdown">
<a class="anchor-target" name="setting-up-the-development-environment"></a>
<a href="#setting-up-the-development-environment"><i class="fas fa-link"></i></a>
Setting up the development environment
</h2>
<p>There are a few technical requirements before we start. Please install the following:</p>
<ul>
<li><a href="https://nodejs.org/en/">Node.js v8+ LTS and npm</a> (comes with Node)</li>
<li><a href="https://git-scm.com/">Git</a></li>
</ul>
<p>Once we have those installed, we only need one command to install Truffle:</p>
<pre><code class="language-shell">npm install -g truffle</code></pre>
<p>To verify that Truffle is installed properly, type <code>truffle version</code> on a terminal. If you see an error, make sure that your npm modules are added to your path.</p>
<p>We also will be using <a href="/ganache">Ganache</a>, a personal blockchain for Ethereum development you can use to deploy contracts, develop applications, and run tests. You can download Ganache by navigating to <a href="http://truffleframework.com/ganache">http://truffleframework.com/ganache</a> and clicking the "Download" button.</p>
<p class="alert alert-info">
<strong>Note</strong>: If you are developing in an environment without a graphical interface, you can also use Truffle Develop, Truffle's built-in personal blockchain, instead of Ganache. You will need to change some settings---such as the port the blockchain runs on---to adapt the tutorial for Truffle Develop.
</p>
<h2 class="link-markdown">
<a class="anchor-target" name="creating-a-truffle-project-using-a-truffle-box"></a>
<a href="#creating-a-truffle-project-using-a-truffle-box"><i class="fas fa-link"></i></a>
Creating a Truffle project using a Truffle Box
</h2>
<ol>
<li><p>Truffle initializes in the current directory, so first create a directory in your development folder of choice and then moving inside it.</p>
<pre><code class="language-shell">mkdir pet-shop-tutorial
cd pet-shop-tutorial</code></pre>
</li>
<li><p>We've created a special <a href="/boxes">Truffle Box</a> just for this tutorial called <code>pet-shop</code>, which includes the basic project structure as well as code for the user interface. Use the <code>truffle unbox</code> command to unpack this Truffle Box.</p>
<pre><code class="language-shell">truffle unbox pet-shop</code></pre>
</li>
</ol>
<p class="alert alert-info">
<strong>Note</strong>: Truffle can be initialized a few different ways. Another useful initialization command is `truffle init`, which creates an empty Truffle project with no example contracts included. For more information, please see the documentation on <a href="/docs/truffle/getting-started/creating-a-project">Creating a project</a>.
</p>
<h3 class="link-markdown">
<a class="anchor-target" name="directory-structure"></a>
<a href="#directory-structure"><i class="fas fa-link"></i></a>
Directory structure
</h3>
<p>The default Truffle directory structure contains the following:</p>
<ul>
<li><code>contracts/</code>: Contains the <a href="https://solidity.readthedocs.io/">Solidity</a> source files for our smart contracts. There is an important contract in here called <code>Migrations.sol</code>, which we'll talk about later.</li>
<li><code>migrations/</code>: Truffle uses a migration system to handle smart contract deployments. A migration is an additional special smart contract that keeps track of changes.</li>
<li><code>test/</code>: Contains both JavaScript and Solidity tests for our smart contracts</li>
<li><code>truffle-config.js</code>: Truffle configuration file</li>
</ul>
<p>The <code>pet-shop</code> Truffle Box has extra files and folders in it, but we won't worry about those just yet.</p>
<h2 class="link-markdown">
<a class="anchor-target" name="writing-the-smart-contract"></a>
<a href="#writing-the-smart-contract"><i class="fas fa-link"></i></a>
Writing the smart contract
</h2>
<p>We'll start our dapp by writing the smart contract that acts as the back-end logic and storage.</p>
<ol>
<li><p>Create a new file named <code>Adoption.sol</code> in the <code>contracts/</code> directory.</p>
</li>
<li><p>Add the following content to the file:</p>
<pre><code class="language-solidity">pragma solidity ^0.5.0;
contract Adoption {
}</code></pre>
</li>
</ol>
<p>Things to notice:</p>
<ul>
<li>The minimum version of Solidity required is noted at the top of the contract: <code>pragma solidity ^0.5.0;</code>. The <code>pragma</code> command means "<em>additional information that only the compiler cares about</em>", while the caret symbol (^) means "<em>the version indicated or higher</em>".</li>
<li>Like JavaScript or PHP, statements are terminated with semicolons.</li>
</ul>
<h3 class="link-markdown">
<a class="anchor-target" name="variable-setup"></a>
<a href="#variable-setup"><i class="fas fa-link"></i></a>
Variable setup
</h3>
<p>Solidity is a statically-typed language, meaning data types like strings, integers, and arrays must be defined. <strong>Solidity has a unique type called an address</strong>. Addresses are Ethereum addresses, stored as 20 byte values. Every account and smart contract on the Ethereum blockchain has an address and can send and receive Ether to and from this address.</p>
<ol>
<li><p>Add the following variable on the next line after <code>contract Adoption {</code>.</p>
<pre><code class="language-solidity">address[16] public adopters;</code></pre>
</li>
</ol>
<p>Things to notice:</p>
<ul>
<li><p>We've defined a single variable: <code>adopters</code>. This is an <strong>array</strong> of Ethereum addresses. Arrays contain one type and can have a fixed or variable length. In this case the type is <code>address</code> and the length is <code>16</code>.</p>
</li>
<li><p>You'll also notice <code>adopters</code> is public. <strong>Public</strong> variables have automatic getter methods, but in the case of arrays a key is required and will only return a single value. Later, we'll write a function to return the whole array for use in our UI.</p>
</li>
</ul>
<h3 class="link-markdown">
<a class="anchor-target" name="your-first-function-adopting-a-pet"></a>
<a href="#your-first-function-adopting-a-pet"><i class="fas fa-link"></i></a>
Your first function: Adopting a pet
</h3>
<p>Let's allow users to make adoption requests.</p>
<ol>
<li><p>Add the following function to the smart contract after the variable declaration we set up above.</p>
<pre><code class="language-solidity">// Adopting a pet
function adopt(uint petId) public returns (uint) {
require(petId >= 0 && petId <= 15);
adopters[petId] = msg.sender;
return petId;
}</code></pre>
</li>
</ol>
<p>Things to notice:</p>
<ul>
<li><p>In Solidity the types of both the function parameters and output must be specified. In this case we'll be taking in a <code>petId</code> (integer) and returning an integer.</p>
</li>
<li><p>We are checking to make sure <code>petId</code> is in range of our <code>adopters</code> array. Arrays in Solidity are indexed from 0, so the ID value will need to be between 0 and 15. We use the <code>require()</code> statement to ensure the ID is within range.</p>
</li>
<li><p>If the ID is in range, we then add the address that made the call to our <code>adopters</code> array. <strong>The address of the person or smart contract who called this function is denoted by <code>msg.sender</code></strong>.</p>
</li>
<li><p>Finally, we return the <code>petId</code> provided as a confirmation.</p>
</li>
</ul>
<!-- ADD SOMETHING ABOUT PUBLIC VIEW? -->
<h3 class="link-markdown">
<a class="anchor-target" name="your-second-function-retrieving-the-adopters"></a>
<a href="#your-second-function-retrieving-the-adopters"><i class="fas fa-link"></i></a>
Your second function: Retrieving the adopters
</h3>
<p>As mentioned above, array getters return only a single value from a given key. Our UI needs to update all pet adoption statuses, but making 16 API calls is not ideal. So our next step is to write a function to return the entire array.</p>
<ol>
<li><p>Add the following <code>getAdopters()</code> function to the smart contract, after the <code>adopt()</code> function we added above:</p>
<pre><code class="language-solidity">// Retrieving the adopters
function getAdopters() public view returns (address[16] memory) {
return adopters;
}</code></pre>
<p>Things to notice:</p>
</li>
</ol>
<ul>
<li><p>Since <code>adopters</code> is already declared, we can simply return it. Be sure to specify the return type (in this case, the type for <code>adopters</code>) as <code>address[16] memory</code>. <code>memory</code> gives the data location for the variable.</p>
</li>
<li><p>The <code>view</code> keyword in the function declaration means that the function will not modify the state of the contract. Further information about the exact limits imposed by view is available <a href="https://solidity.readthedocs.io/en/latest/contracts.html#view-functions">here</a>.</p>
</li>
</ul>
<h2 class="link-markdown">
<a class="anchor-target" name="compiling-and-migrating-the-smart-contract"></a>
<a href="#compiling-and-migrating-the-smart-contract"><i class="fas fa-link"></i></a>
Compiling and migrating the smart contract
</h2>
<p>Now that we have written our smart contract, the next steps are to compile and migrate it.</p>
<h3 class="link-markdown">
<a class="anchor-target" name="compilation"></a>
<a href="#compilation"><i class="fas fa-link"></i></a>
Compilation
</h3>
<p>Solidity is a compiled language, meaning we need to compile our Solidity to bytecode for the Ethereum Virtual Machine (EVM) to execute. Think of it as translating our human-readable Solidity into something the EVM understands.</p>
<ol>
<li><p>In a terminal, make sure you are in the root of the directory that contains the dapp and type:</p>
<pre><code class="language-shell">truffle compile</code></pre>
<p class="alert alert-info">
<strong>Note</strong>: If you're on Windows and encountering problems running this command, please see the documentation on <a href="/docs/truffle/reference/configuration#resolving-naming-conflicts-on-windows">resolving naming conflicts on Windows</a>.
</p>
<p>You should see output similar to the following:</p>
<pre><code class="language-shell">Compiling your contracts...
===========================
> Compiling ./contracts/Adoption.sol
> Compiling ./contracts/Migrations.sol
> Artifacts written to /Users/cruzmolina/Code/truffle-projects/metacoin/build/contracts
> Compiled successfully using:
- solc: 0.5.0+commit.1d4f565a.Emscripten.clang</code></pre>
</li>
</ol>
<h3 class="link-markdown">
<a class="anchor-target" name="migration"></a>
<a href="#migration"><i class="fas fa-link"></i></a>
Migration
</h3>
<p>Now that we've successfully compiled our contracts, it's time to migrate them to the blockchain!</p>
<p><strong>A migration is a deployment script meant to alter the state of your application's contracts</strong>, moving it from one state to the next. For the first migration, you might just be deploying new code, but over time, other migrations might move data around or replace a contract with a new one.</p>
<p class="alert alert-info">
<strong>Note</strong>: Read more about migrations in the <a href="/docs/truffle/getting-started/running-migrations">Truffle documentation</a>.
</p>
<p>You'll see one JavaScript file already in the <code>migrations/</code> directory: <code>1_initial_migration.js</code>. This handles deploying the <code>Migrations.sol</code> contract to observe subsequent smart contract migrations, and ensures we don't double-migrate unchanged contracts in the future.</p>
<p>Now we are ready to create our own migration script.</p>
<ol>
<li><p>Create a new file named <code>2_deploy_contracts.js</code> in the <code>migrations/</code> directory.</p>
</li>
<li><p>Add the following content to the <code>2_deploy_contracts.js</code> file:</p>
<pre><code class="language-javascript">var Adoption = artifacts.require("Adoption");
module.exports = function(deployer) {
deployer.deploy(Adoption);
};</code></pre>
</li>
<li><p>Before we can migrate our contract to the blockchain, we need to have a blockchain running. For this tutorial, we're going to use <a href="/ganache">Ganache</a>, a personal blockchain for Ethereum development you can use to deploy contracts, develop applications, and run tests. If you haven't already, <a href="/ganache">download Ganache</a> and double click the icon to launch the application. This will generate a blockchain running locally on port 7545.</p>
<p class="alert alert-info">
<strong>Note</strong>: Read more about Ganache in the <a href="/docs/ganache/using">Truffle documentation</a>.
</p>
<p>
<img class="img-fluid" src="/img/tutorials/pet-shop/ganache-initial.png" title="Ganache on first launch" alt="Ganache on first launch" />
<p class="img-caption">Ganache on first launch</p>
</p>
</li>
<li><p>Back in our terminal, migrate the contract to the blockchain.</p>
<pre><code class="language-shell">truffle migrate</code></pre>
<p>You should see output similar to the following:</p>
</li>
</ol>
<pre><code class="language-shell"> 1_initial_migration.js
======================
Deploying 'Migrations'
----------------------
> transaction hash: 0x3b558e9cdf1231d8ffb3445cb2f9fb01de9d0363e0b97a17f9517da318c2e5af
> Blocks: 0 Seconds: 0
> contract address: 0x5ccb4dc04600cffA8a67197d5b644ae71856aEE4
> account: 0x8d9606F90B6CA5D856A9f0867a82a645e2DfFf37
> balance: 99.99430184
> gas used: 284908
> gas price: 20 gwei
> value sent: 0 ETH
> total cost: 0.00569816 ETH
> Saving migration to chain.
> Saving artifacts
-------------------------------------
> Total cost: 0.00569816 ETH
2_deploy_contracts.js
=====================
Deploying 'Adoption'
.............................
.............................</code></pre>
<p> You can see the migrations being executed in order, followed by some information related to each migration. (Your information will differ.)</p>
<ol>
<li><p>In Ganache, note that the state of the blockchain has changed. The blockchain now shows that the current block, previously <code>0</code>, is now <code>4</code>. In addition, while the first account originally had 100 ether, it is now lower, due to the transaction costs of migration. We'll talk more about transaction costs later.</p>
<p>
<img class="img-fluid" src="/img/tutorials/pet-shop/ganache-migrated.png" title="Ganache after migration" alt="Ganache after migration" />
<p class="img-caption">Ganache after migration</p>
</p>
</li>
</ol>
<p>You've now written your first smart contract and deployed it to a locally running blockchain. It's time to interact with our smart contract now to make sure it does what we want.</p>
<h2 class="link-markdown">
<a class="anchor-target" name="testing-the-smart-contract-using-solidity"></a>
<a href="#testing-the-smart-contract-using-solidity"><i class="fas fa-link"></i></a>
Testing the smart contract using Solidity
</h2>
<details>
<summary> Expand This Section </summary>
Truffle is very flexible when it comes to smart contract testing, in that tests can be written either in JavaScript or Solidity. In this tutorial, we'll be writing our tests in Solidity.
<ol>
<li><p>Create a new file named <code>TestAdoption.sol</code> in the <code>test/</code> directory.</p>
</li>
<li><p>Add the following content to the <code>TestAdoption.sol</code> file:</p>
<pre><code class="language-solidity">pragma solidity ^0.5.0;
import "truffle/Assert.sol";
import "truffle/DeployedAddresses.sol";
import "../contracts/Adoption.sol";
contract TestAdoption {
// The address of the adoption contract to be tested
Adoption adoption = Adoption(DeployedAddresses.Adoption());
// The id of the pet that will be used for testing
uint expectedPetId = 8;
//The expected owner of adopted pet is this contract
address expectedAdopter = address(this);
}</code></pre>
</li>
</ol>
<p>We start the contract off with 3 imports:</p>
<ul>
<li><code>Assert.sol</code>: Gives us various assertions to use in our tests. In testing, <strong>an assertion checks for things like equality, inequality or emptiness to return a pass/fail</strong> from our test. <a href="https://github.com/trufflesuite/truffle/blob/master/packages/core/lib/testing/Assert.sol">Here's a full list of the assertions included with Truffle</a>.</li>
<li><code>DeployedAddresses.sol</code>: When running tests, Truffle will deploy a fresh instance of the contract being tested to the blockchain. This smart contract gets the address of the deployed contract.</li>
<li><code>Adoption</code>: The smart contract we want to test.</li>
</ul>
<p class="alert alert-info">
<strong>Note</strong>: The first two imports are referring to global Truffle files, not a `truffle` directory. You should not see a `truffle` directory inside your `test/` directory.
</p>
<p>Then we define three contract-wide variables:</p>
<ul>
<li>First, one containing the smart contract to be tested, calling the <code>DeployedAddresses</code> smart contract to get its address.</li>
<li>Second, the id of the pet that will be used to test the adoption functions.</li>
<li>Third, since the TestAdoption contract will be sending the transaction, we set the expected adopter address to <strong>this</strong>, a contract-wide variable that gets the current contract's address.</li>
</ul>
<h3 class="link-markdown">
<a class="anchor-target" name="testing-the-adopt-function"></a>
<a href="#testing-the-adopt-function"><i class="fas fa-link"></i></a>
Testing the adopt() function
</h3>
<p>To test the <code>adopt()</code> function, recall that upon success it returns the given <code>petId</code>. We can ensure an ID was returned and that it's correct by comparing the return value of <code>adopt()</code> to the ID we passed in.</p>
<ol>
<li><p>Add the following function within the <code>TestAdoption.sol</code> smart contract, after the declaration of <code>Adoption</code>:</p>
<pre><code class="language-solidity">// Testing the adopt() function
function testUserCanAdoptPet() public {
uint returnedId = adoption.adopt(expectedPetId);
Assert.equal(returnedId, expectedPetId, "Adoption of the expected pet should match what is returned.");
}</code></pre>
</li>
</ol>
<p>Things to notice:</p>
<ul>
<li>We call the smart contract we declared earlier with the ID of <code>expectedPetId</code>.</li>
<li>Finally, we pass the actual value, the expected value and a failure message (which gets printed to the console if the test does not pass) to <code>Assert.equal()</code>.</li>
</ul>
<h3 class="link-markdown">
<a class="anchor-target" name="testing-retrieval-of-a-single-pet-39-s-owner"></a>
<a href="#testing-retrieval-of-a-single-pet-39-s-owner"><i class="fas fa-link"></i></a>
Testing retrieval of a single pet's owner
</h3>
<p>Remembering from above that public variables have automatic getter methods, we can retrieve the address stored by our adoption test above. Stored data will persist for the duration of our tests, so our adoption of pet <code>expectedPetId</code> above can be retrieved by other tests.</p>
<ol>
<li><p>Add this function below the previously added function in <code>TestAdoption.sol</code>.</p>
<pre><code class="language-solidity">// Testing retrieval of a single pet's owner
function testGetAdopterAddressByPetId() public {
address adopter = adoption.adopters(expectedPetId);
Assert.equal(adopter, expectedAdopter, "Owner of the expected pet should be this contract");
}</code></pre>
</li>
</ol>
<p>After getting the adopter address stored by the adoption contract, we assert equality as we did above.</p>
<h3 class="link-markdown">
<a class="anchor-target" name="testing-retrieval-of-all-pet-owners"></a>
<a href="#testing-retrieval-of-all-pet-owners"><i class="fas fa-link"></i></a>
Testing retrieval of all pet owners
</h3>
<p>Since arrays can only return a single value given a single key, we create our own getter for the entire array.</p>
<ol>
<li><p>Add this function below the previously added function in <code>TestAdoption.sol</code>.</p>
<pre><code class="language-solidity">// Testing retrieval of all pet owners
function testGetAdopterAddressByPetIdInArray() public {
// Store adopters in memory rather than contract's storage
address[16] memory adopters = adoption.getAdopters();
Assert.equal(adopters[expectedPetId], expectedAdopter, "Owner of the expected pet should be this contract");
}</code></pre>
</li>
</ol>
<p>Note the <strong>memory</strong> attribute on <code>adopters</code>. The memory attribute tells Solidity to temporarily store the value in memory, rather than saving it to the contract's storage. Since <code>adopters</code> is an array, and we know from the first adoption test that we adopted pet <code>expectedPetId</code>, we compare the testing contracts address with location <code>expectedPetId</code> in the array.</p>
</details>
<h2 class="link-markdown">
<a class="anchor-target" name="testing-the-smart-contract-using-javascript"></a>
<a href="#testing-the-smart-contract-using-javascript"><i class="fas fa-link"></i></a>
Testing the smart contract using JavaScript
</h2>
<details>
<summary> Expand This Section </summary>
Truffle is very flexible when it comes to smart contract testing, in that tests can be written either in JavaScript or Solidity. In this tutorial, we'll be writing our tests in Javascript using the Chai and Mocha libraries.<br/>
<ol>
<li><p>Create a new file named <code>testAdoption.test.js</code> in the <code>test/</code> directory.<br/></p>
</li>
<li><p>Add the following content to the <code>testAdoption.test.js</code> file:<br/></p>
<pre><code>const Adoption = artifacts.require("Adoption");
contract("Adoption", (accounts) => {
let adoption;
let expectedAdopter;
before(async () => {
adoption = await Adoption.deployed();
});
describe("adopting a pet and retrieving account addresses", async () => {
before("adopt a pet using accounts[0]", async () => {
await adoption.adopt(8, { from: accounts[0] });
expectedAdopter = accounts[0];
});
});
});
</code></pre><p>We start the contract by importing : </p>
<ul>
<li><code>Adoption</code>: The smart contract we want to test
We begin our test by importing our <code>Adoption</code> contract using <code>artifacts.require</code>.</li>
</ul>
<p><strong>Note</strong>: When writing this test, our callback function take the argument <code>accounts</code>. This provides us with the accounts available on the network when using this test.</p>
<p>Then, we make use of the <code>before</code> to provide initial setups for the following: </p>
<ul>
<li>Adopt a pet with id 8 and assign it to the first account within the test accounts on the network.</li>
<li>This function later is used to check whether the <code>petId: 8</code> has been adopted by <code>accounts[0]</code>.</li>
</ul>
<h3 class="link-markdown">
<a class="anchor-target" name="testing-the-adopt-function"></a>
<a href="#testing-the-adopt-function"><i class="fas fa-link"></i></a>
Testing the adopt function
</h3>
<p>To test the <code>adopt</code> function, recall that upon success it returns the given <code>adopter</code>. We can ensure that the adopter based on given petID was returned and is compared with the <code>expectedAdopter</code> within the <code>adopt</code> function.</p>
<ol>
<li>Add the following function within the <code>testAdoption.test.js</code> test file, after the declaration of <code>before</code> code block. </li>
</ol>
<pre><code>describe("adopting a pet and retrieving account addresses", async () => {
before("adopt a pet using accounts[0]", async () => {
await adoption.adopt(8, { from: accounts[0] });
expectedAdopter = accounts[0];
});
it("can fetch the address of an owner by pet id", async () => {
const adopter = await adoption.adopters(8);
assert.equal(adopter, expectedAdopter, "The owner of the adopted pet should be the first account.");
});
});</code></pre><p>Things to notice:</p>
<ul>
<li>We call smart contract method <code>adopters</code> to see what address adopted the pet with <code>petID</code> 8.</li>
<li>Truffle imports <code>Chai</code> for the user so we can use the <code>assert</code> functions. We pass the actual value, the expected value and a failure message (which gets printed to the console if the test does not pass) to <code>assert.equal()</code>.</li>
</ul>
<h3 class="link-markdown">
<a class="anchor-target" name="testing-retrieval-of-all-pet-owners"></a>
<a href="#testing-retrieval-of-all-pet-owners"><i class="fas fa-link"></i></a>
Testing retrieval of all pet owners
</h3>
<p>Since arrays can only return a single value given a single key, we create our own getter for the entire array.</p>
<ol>
<li>Add this function below the previously added function in <code>testAdoption.test.js</code>.</li>
</ol>
<pre><code>it("can fetch the collection of all pet owners' addresses", async () => {
const adopters = await adoption.getAdopters();
assert.equal(adopters[8], expectedAdopter, "The owner of the adopted pet should be in the collection.");
});</code></pre><p>Since adopters is an array, and we know from the first adoption test that we adopted the pet with <code>petId</code> 8, we are comparing the contract's address with the address that we expect to find.</p>
</details>
</li>
</ol>
<h3 class="link-markdown">
<a class="anchor-target" name="running-the-tests"></a>
<a href="#running-the-tests"><i class="fas fa-link"></i></a>
Running the tests
</h3>
<ol>
<li><p>Back in the terminal, run the tests:</p>
<pre><code class="language-shell">truffle test</code></pre>
</li>
<li><p>If all the tests pass, you'll see console output similar to this:</p>
</li>
</ol>
<pre><code class="language-shell"> Using network 'development'.
Compiling your contracts...
===========================
> Compiling ./test/TestAdoption.sol
> Artifacts written to /var/folders/z3/v0sd04ys11q2sh8tq38mz30c0000gn/T/test-11934-19747-g49sra.0ncrr
> Compiled successfully using:
- solc: 0.5.0+commit.1d4f565a.Emscripten.clang
TestAdoption
✓ testUserCanAdoptPet (91ms)
✓ testGetAdopterAddressByPetId (70ms)
✓ testGetAdopterAddressByPetIdInArray (89ms)
3 passing (670ms)</code></pre>
<h2 class="link-markdown">
<a class="anchor-target" name="creating-a-user-interface-to-interact-with-the-smart-contract"></a>
<a href="#creating-a-user-interface-to-interact-with-the-smart-contract"><i class="fas fa-link"></i></a>
Creating a user interface to interact with the smart contract
</h2>
<p>Now that we've created the smart contract, deployed it to our local test blockchain and confirmed we can interact with it via the console, it's time to create a UI so that Pete has something to use for his pet shop!</p>
<p>Included with the <code>pet-shop</code> Truffle Box was code for the app's front-end. That code exists within the <code>src/</code> directory.</p>
<p>The front-end doesn't use a build system (webpack, grunt, etc.) to be as easy as possible to get started. The structure of the app is already there; we'll be filling in the functions which are unique to Ethereum. This way, you can take this knowledge and apply it to your own front-end development.</p>
<h3 class="link-markdown">
<a class="anchor-target" name="instantiating-web3"></a>
<a href="#instantiating-web3"><i class="fas fa-link"></i></a>
Instantiating web3
</h3>
<ol>
<li><p>Open <code>/src/js/app.js</code> in a text editor.</p>
</li>
<li><p>Examine the file. Note that there is a global <code>App</code> object to manage our application, load in the pet data in <code>init()</code> and then call the function <code>initWeb3()</code>. The <a href="https://github.com/ethereum/web3.js/">web3 JavaScript library</a> interacts with the Ethereum blockchain. It can retrieve user accounts, send transactions, interact with smart contracts, and more.</p>
</li>
<li><p>Remove the multi-line comment from within <code>initWeb3</code> and replace it with the following:</p>
<pre><code class="language-javascript">// Modern dapp browsers...
if (window.ethereum) {
App.web3Provider = window.ethereum;
try {
// Request account access
await window.ethereum.enable();
} catch (error) {
// User denied account access...
console.error("User denied account access")
}
}
// Legacy dapp browsers...
else if (window.web3) {
App.web3Provider = window.web3.currentProvider;
}
// If no injected web3 instance is detected, fall back to Ganache
else {
App.web3Provider = new Web3.providers.HttpProvider('http://localhost:7545');
}
web3 = new Web3(App.web3Provider);</code></pre>
</li>
</ol>
<p>Things to notice:</p>
<ul>
<li><p>First, we check if we are using modern dapp browsers or the more recent versions of <a href="https://github.com/MetaMask">MetaMask</a> where an <code>ethereum</code> provider is injected into the <code>window</code> object. If so, we use it to create our web3 object, but we also need to explicitly request access to the accounts with <code>ethereum.enable()</code>.</p>
</li>
<li><p>If the <code>ethereum</code> object does not exist, we then check for an injected <code>web3</code> instance. If it exists, this indicates that we are using an older dapp browser (like <a href="https://github.com/ethereum/mist">Mist</a> or an older version of MetaMask). If so, we get its provider and use it to create our web3 object.</p>
</li>
<li><p>If no injected web3 instance is present, we create our web3 object based on our local provider. (This fallback is fine for development environments, but insecure and not suitable for production.)</p>
</li>
</ul>
<h3 class="link-markdown">
<a class="anchor-target" name="instantiating-the-contract"></a>
<a href="#instantiating-the-contract"><i class="fas fa-link"></i></a>
Instantiating the contract
</h3>
<p>Now that we can interact with Ethereum via web3, we need to instantiate our smart contract so web3 knows where to find it and how it works. Truffle has a library to help with this called <code>@truffle/contract</code>. It keeps information about the contract in sync with migrations, so you don't need to change the contract's deployed address manually.</p>
<ol>
<li><p>Still in <code>/src/js/app.js</code>, remove the multi-line comment from within <code>initContract</code> and replace it with the following:</p>
<pre><code class="language-javascript">$.getJSON('Adoption.json', function(data) {
// Get the necessary contract artifact file and instantiate it with @truffle/contract
var AdoptionArtifact = data;
App.contracts.Adoption = TruffleContract(AdoptionArtifact);
// Set the provider for our contract
App.contracts.Adoption.setProvider(App.web3Provider);
// Use our contract to retrieve and mark the adopted pets
return App.markAdopted();
});</code></pre>
</li>
</ol>
<p>Things to notice:</p>
<ul>
<li><p>We first retrieve the artifact file for our smart contract. <strong>Artifacts are information about our contract such as its deployed address and Application Binary Interface (ABI)</strong>. <strong>The ABI is a JavaScript object defining how to interact with the contract including its variables, functions and their parameters.</strong></p>
</li>
<li><p>Once we have the artifacts in our callback, we pass them to <code>TruffleContract()</code>. This creates an instance of the contract we can interact with.</p>
</li>
<li><p>With our contract instantiated, we set its web3 provider using the <code>App.web3Provider</code> value we stored earlier when setting up web3.</p>
</li>
<li><p>We then call the app's <code>markAdopted()</code> function in case any pets are already adopted from a previous visit. We've encapsulated this in a separate function since we'll need to update the UI any time we make a change to the smart contract's data.</p>
</li>
</ul>
<h3 class="link-markdown">
<a class="anchor-target" name="getting-the-adopted-pets-and-updating-the-ui"></a>
<a href="#getting-the-adopted-pets-and-updating-the-ui"><i class="fas fa-link"></i></a>
Getting The Adopted Pets and Updating The UI
</h3>
<ol>
<li><p>Still in <code>/src/js/app.js</code>, remove the multi-line comment from <code>markAdopted</code> and replace it with the following:</p>
<pre><code class="language-javascript">var adoptionInstance;
App.contracts.Adoption.deployed().then(function(instance) {
adoptionInstance = instance;
return adoptionInstance.getAdopters.call();
}).then(function(adopters) {
for (i = 0; i < adopters.length; i++) {
if (adopters[i] !== '0x0000000000000000000000000000000000000000') {
$('.panel-pet').eq(i).find('button').text('Success').attr('disabled', true);
}
}
}).catch(function(err) {
console.log(err.message);
});</code></pre>
</li>
</ol>
<p>Things to notice:</p>
<ul>
<li><p>We access the deployed <code>Adoption</code> contract, then call <code>getAdopters()</code> on that instance.</p>
</li>
<li><p>We first declare the variable <code>adoptionInstance</code> outside of the smart contract calls so we can access the instance after initially retrieving it.</p>
</li>
<li><p>Using <strong>call()</strong> allows us to read data from the blockchain without having to send a full transaction, meaning we won't have to spend any ether.</p>
</li>
<li><p>After calling <code>getAdopters()</code>, we then loop through all of them, checking to see if an address is stored for each pet. Since the array contains address types, Ethereum initializes the array with 16 empty addresses. This is why we check for an empty address string rather than null or other falsey value.</p>
</li>
<li><p>Once a <code>petId</code> with a corresponding address is found, we disable its adopt button and change the button text to "Success", so the user gets some feedback.</p>
</li>
<li><p>Any errors are logged to the console.</p>
</li>
</ul>
<h3 class="link-markdown">
<a class="anchor-target" name="handling-the-adopt-function"></a>
<a href="#handling-the-adopt-function"><i class="fas fa-link"></i></a>
Handling the adopt() Function
</h3>
<ol>
<li><p>Still in <code>/src/js/app.js</code>, remove the multi-line comment from <code>handleAdopt</code> and replace it with the following:</p>
<pre><code class="language-javascript">var adoptionInstance;
web3.eth.getAccounts(function(error, accounts) {
if (error) {
console.log(error);
}
var account = accounts[0];
App.contracts.Adoption.deployed().then(function(instance) {
adoptionInstance = instance;
// Execute adopt as a transaction by sending account
return adoptionInstance.adopt(petId, {from: account});
}).then(function(result) {
return App.markAdopted();
}).catch(function(err) {
console.log(err.message);
});
});</code></pre>
</li>
</ol>
<p>Things to notice:</p>
<ul>
<li><p>We use web3 to get the user's accounts. In the callback after an error check, we then select the first account.</p>
</li>
<li><p>From there, we get the deployed contract as we did above and store the instance in <code>adoptionInstance</code>. This time though, we're going to send a <strong>transaction</strong> instead of a call. Transactions require a "from" address and have an associated cost. This cost, paid in ether, is called <strong>gas</strong>. The gas cost is the fee for performing computation and/or storing data in a smart contract. We send the transaction by executing the <code>adopt()</code> function with both the pet's ID and an object containing the account address, which we stored earlier in <code>account</code>.</p>
</li>
<li><p>The result of sending a transaction is the transaction object. If there are no errors, we proceed to call our <code>markAdopted()</code> function to sync the UI with our newly stored data.</p>
</li>
</ul>
<h2 class="link-markdown">
<a class="anchor-target" name="interacting-with-the-dapp-in-a-browser"></a>
<a href="#interacting-with-the-dapp-in-a-browser"><i class="fas fa-link"></i></a>
Interacting with the dapp in a browser
</h2>
<p>Now we're ready to use our dapp!</p>
<h3 class="link-markdown">
<a class="anchor-target" name="installing-and-configuring-metamask"></a>
<a href="#installing-and-configuring-metamask"><i class="fas fa-link"></i></a>
Installing and configuring MetaMask
</h3>
<p>The easiest way to interact with our dapp in a browser is through <a href="https://metamask.io/">MetaMask</a>, a browser extension for both Chrome and Firefox.</p>
<ol>
<li><p>Install MetaMask in your browser.</p>
</li>
<li><p>Once installed, a tab in your browser should open displaying the following:</p>
<p>
<img class="img-fluid" src="/img/tutorials/pet-shop/metamask-welcome.png" title="Welcome to MetaMask" alt="Welcome to MetaMask" />
<p class="img-caption">Welcome to MetaMask</p>
</p>
</li>
<li><p>After clicking <strong>Getting Started</strong>, you should see the initial MetaMask screen. Click <strong>Import Wallet</strong>.</p>
<p>
<img class="img-fluid" src="/img/tutorials/pet-shop/metamask-initial.png" title="Initial screen" alt="Initial screen" />
<p class="img-caption">MetaMask initial screen</p>
</p>
</li>
<li><p>Next, you should see a screen requesting anonymous analytics. Choose to decline or agree.</p>
<p>
<img class="img-fluid" src="/img/tutorials/pet-shop/metamask-analytics.png" title="Improve MetaMask" alt="Improve MetaMask" />
<p class="img-caption">Improve MetaMask</p>
</p>
</li>
<li><p>In the box marked <strong>Wallet Seed</strong>, enter the mnemonic that is displayed in Ganache.</p>
<p class="alert alert-danger">
**Warning**: Do not use this mnemonic on the main Ethereum network (mainnet). If you send ETH to any account generated from this mnemonic, you will lose it all!
</p>
<p>Enter a password below that and click <strong>OK</strong>.</p>
<p>
<img class="img-fluid" src="/img/tutorials/pet-shop/metamask-seed.png" title="MetaMask seed phrase" alt="MetaMask seed phrase" />
<p class="img-caption">MetaMask seed phrase</p>
</p>
</li>
<li><p>If all goes well, MetaMask should display the following screen. Click <strong>All Done</strong>.</p>
<p>
<img class="img-fluid" src="/img/tutorials/pet-shop/metamask-congratulations.png" title="Congratulations" alt="Congratulations" />
<p class="img-caption">Congratulations</p>
</p>
</li>
<li><p>Now we need to connect MetaMask to the blockchain created by Ganache. Click the menu that shows "Main Network" and select <strong>Custom RPC</strong>.</p>
<p>
<img class="img-fluid" src="/img/tutorials/pet-shop/metamask-networkmenu.png" title="MetaMask network menu" alt="MetaMask network menu" />
<p class="img-caption">MetaMask network menu</p>
</p>
</li>
<li><p>In the box titled "New Network" enter <code>http://127.0.0.1:7545</code>, in the box titled "Chain ID" enter <code>1337</code> (Default Chain ID for Ganache) and click <strong>Save</strong>.</p>
<p>
<img class="img-fluid" src="/img/tutorials/pet-shop/metamask-customrpc.png" title="MetaMask Custom RPC" alt="MetaMask Custom RPC" />
<p class="img-caption">MetaMask Custom RPC</p>
</p>
<p>The network name at the top will switch to say <code>http://127.0.0.1:7545</code>.</p>
</li>
<li><p>Click the top-right X to close out of Settings and return to the Accounts page.</p>
<p>Each account created by Ganache is given 100 ether. You'll notice it's slightly less on the first account because some gas was used when the contract itself was deployed and when the tests were run.</p>
<p>
<img class="img-fluid" src="/img/tutorials/pet-shop/metamask-account1.png" title="MetaMask account configured" alt="MetaMask account configured" />
<p class="img-caption">MetaMask account configured</p>
</p>
<p>Configuration is now complete.</p>
</li>
</ol>
<h3 class="link-markdown">
<a class="anchor-target" name="installing-and-configuring-lite-server"></a>
<a href="#installing-and-configuring-lite-server"><i class="fas fa-link"></i></a>
Installing and configuring lite-server
</h3>
<p>We can now start a local web server and use the dapp. We're using the <code>lite-server</code> library to serve our static files. This shipped with the <code>pet-shop</code> Truffle Box, but let's take a look at how it works.</p>
<ol>
<li><p>Open <code>bs-config.json</code> in a text editor (in the project's root directory) and examine the contents:</p>
<pre><code class="language-javascript">{
"server": {
"baseDir": ["./src", "./build/contracts"]
}
}</code></pre>
<p>This tells <code>lite-server</code> which files to include in our base directory. We add the <code>./src</code> directory for our website files and <code>./build/contracts</code> directory for the contract artifacts.</p>
<p>We've also added a <code>dev</code> command to the <code>scripts</code> object in the <code>package.json</code> file in the project's root directory. The <code>scripts</code> object allows us to alias console commands to a single npm command. In this case we're just doing a single command, but it's possible to have more complex configurations. Here's what yours should look like:</p>
<pre><code class="language-javascript">"scripts": {
"dev": "lite-server",
"test": "echo \"Error: no test specified\" && exit 1"
},</code></pre>
<p>This tells npm to run our local install of <code>lite-server</code> when we execute <code>npm run dev</code> from the console.</p>
</li>
</ol>
<h3 class="link-markdown">
<a class="anchor-target" name="using-the-dapp"></a>
<a href="#using-the-dapp"><i class="fas fa-link"></i></a>
Using the dapp
</h3>
<ol>
<li><p>Start the local web server:</p>
<pre><code class="language-shell">npm run dev</code></pre>
<p>The dev server will launch and automatically open a new browser tab containing your dapp.</p>
<p>
<img class="img-fluid" src="/img/tutorials/pet-shop/dapp.png" title="Pete's Pet Shop" alt="Pete's Pet Shop" />
<p class="img-caption">Pete's Pet Shop</p>
</p>
</li>
<li><p>A MetaMask pop-up should appear requesting your approval to allow Pete's Pet Shop to connect to your MetaMask wallet. Without explicit approval, you will be unable to interact with the dapp. Click <strong>Connect</strong>.</p>
<p>
<img class="img-fluid" src="/img/tutorials/pet-shop/metamask-transactionconfirm.png" title="MetaMask approval request" alt="MetaMask approval request" />
<p class="img-caption">MetaMask approval request</p>
</p>
</li>
<li><p>To use the dapp, click the <strong>Adopt</strong> button on the pet of your choice.</p>
</li>
<li><p>You'll be automatically prompted to approve the transaction by MetaMask. Click <strong>Submit</strong> to approve the transaction.</p>
<p>
<img class="img-fluid" src="/img/tutorials/pet-shop/metamask-transactionconfirm.png" title="Adoption transaction review" alt="Adoption transaction review" />
<p class="img-caption">Adoption transaction review</p>
</p>
</li>
<li><p>You'll see the button next to the adopted pet change to say "Success" and become disabled, just as we specified, because the pet has now been adopted.</p>
<p>
<img class="img-fluid" src="/img/tutorials/pet-shop/dapp-success.png" title="Adoption success" alt="Adoption success" />
<p class="img-caption">Adoption success</p>
</p>
<p class="alert alert-info">
<strong>Note</strong>: If the button doesn't automatically change to say "Success", refreshing the app in the browser should trigger it.
</p>
<p>And in MetaMask, you'll see the transaction listed:</p>
<p>
<img class="img-fluid" src="/img/tutorials/pet-shop/metamask-transactionsuccess.png" title="MetaMask transaction" alt="MetaMask transaction" />
<p class="img-caption">MetaMask transaction</p>
</p>
<p>You'll also see the same transaction listed in Ganache under the "Transactions" section.</p>
</li>
</ol>
<p>Congratulations! You have taken a huge step to becoming a full-fledged dapp developer. For developing locally, you have all the tools you need to start making more advanced dapps. If you'd like to make your dapp live for others to use, stay tuned for our future tutorial on deploying to the Ropsten testnet.</p>
<div class="contribute-link">
See a way to make this page better? <a href="https://github.com/trufflesuite/trufflesuite.com/edit/develop/src/">Edit here »</a>
</div> </div>
</div>
</div>
</main>
<footer>
<div class="container">
<div class="row">
<div class="col-12 col-md-2 mb-4 mb-md-0">
<img class="truffle-logo" src="/img/truffle-logo-light.svg" alt="Truffle Logo" />
</div>
<div class="col-12 col-md-5 mb-3 mb-md-0">
<ul>
<li><a href="mailto:[email protected]">CONTACT US</a></li>
<li><a href="/policy">PRIVACY POLICY</a></li>
<li><a href="/press-releases">PRESS RELEASES</a></li>
<li><a href="/staff">STAFF</a></li>
<li><a href="/dashboard">DASHBOARD</a></li>
<li><a href="https://github.com/trufflesuite">GITHUB</a></li>
<li><a class="trufflecon-link" href="https://www.trufflesuite.com/trufflecon2020">TRUFFLEC<img src="/img/truffle-logomark.svg" alt="Truffle Logo" /><span>O</span>N 2020</a></li>
<li><strong>© ConsenSys Software Inc. 2021</strong></li>
</ul>
<ul class="list-inline h4 ">
<li class="list-inline-item mb-0"><a href="https://www.reddit.com/r/truffle"><i class="fab fa-reddit-square"></i></a></li>
<li class="list-inline-item mb-0"><a href="https://twitter.com/trufflesuite"><i class="fab fa-twitter-square"></i></a></li>
<li class="list-inline-item mb-0"><a href="https://www.facebook.com/trufflesuite/"><i class="fab fa-facebook-square"></i></a></li>