Tutorials These tutorials can help you get started developing different kinds of applications on Tezos in as little as 15 minutes. import TutorialCard from '@site/src/components/TutorialCard'; import TutorialCardContainer from '@site/src/components/TutorialCardContainer'; Beginner These tutorials are intended for developers who are starting work with Tezos: Intermediate These tutorials contain multiple parts and are intended for developers with some application development experience: Advanced These tutorials are intended for developers who are familiar with Tezos and want to get into more powerful applications: Deploy a smart contract This tutorial covers deploying a smart contract to Tezos. It covers how to: Connect to a testnet Create a wallet Get tokens from a faucet Code a contract, including: Defining the storage for the contract Defining entrypoints in the contract Writing code to run when the entrypoints are called Deploy (or originate) the contract to Tezos and set its starting storage value Look up the current state of the contract Call the contract This tutorial has different versions for different programming languages. You can run the tutorial with the version of the language you are most familiar with or want to learn. You do not need an experience in these languages to run the tutorial. To use SmartPy, a language similar to Python, see Deploy a smart contract with SmartPy To use JsLIGO, a language similar to JavaScript and TypeScript, see Deploy a smart contract with JsLIGO To use CameLIGO, a language similar to OCaml, see Deploy a smart contract with CameLIGO To learn the Archetype language, try Deploy a smart contract with Archetype. Deploy a smart contract with JsLIGO This tutorial covers writing and deploying a simple smart contract with the LIGO programming language. Specifically, this tutorial uses the JsLIGO version of LIGO, which has syntax similar to JavaScript, but you don't need any experience with JavaScript or LIGO to do this tutorial. If you are more familiar with Python, try Deploy a smart contract with SmartPy. If you are more familiar with OCaml, try Deploy a smart contract with CameLIGO. To learn the Archetype language, try Deploy a smart contract with Archetype. LIGO is a high-level programming language that you can use to write smart contracts for the Tezos blockchain. It abstracts away the complexity of using Michelson (the smart contract language directly available on-chain) to make it easier to write smart contracts on Tezos. In this tutorial, you will learn how to: Create a wallet to store cryptocurrency tokens Get free tez tokens (the native cryptocurrency token on Tezos) from a faucet Code a contract in LIGO, including: Defining the storage for the contract Defining entrypoints in the contract Writing code to run when the entrypoints are called Deploy (or originate) the contract to Tezos and set its starting storage value Look up the current state of the contract Call the contract What is a smart contract? A smart contract is a computer program that is stored on a blockchain and runs on a blockchain. Because the blockchain is spread across many computer nodes, you don't have to think about where to host the program or worry whether a computer will run it or not. Responsibility for running the contract is distributed across all of the nodes in the Tezos system, so when you deploy a smart contract, you can be confident that it will be available and unmodified when someone wants to run it. A smart contract has these parts: Persistent storage, data that the contract can read and write One or more entrypoints, which are a kind of function that clients can call, like endpoints in an API or functions or methods in many programming languages A Tezos account that can store tokens (technically, the contract is itself a type of Tezos account, but you can think of it as a program with a Tezos account) Tutorial contract The contract that you deploy in this tutorial stores a single integer. It provides entrypoints that clients can call to change the value of that integer: The increment entrypoint accepts an integer as a parameter and adds that integer to the value in storage The decrement entrypoint accepts an integer as a parameter and subtracts that integer from the value in storage The reset entrypoint takes no parameters and resets the value in storage to 0 After you deploy the contract, you or any other user can call it from various sources, including web applications, other contracts, and the Octez command-line client. However, no one can prevent it from running or tamper with its code or its storage. Creating and funding a wallet To deploy and work with the contract, you need a wallet and some tez tokens. You can get test tokens for free on the Ghostnet test network: Install a Tezos-compatible wallet. Which wallet you install is up to you and whether you want to install a wallet on your computer, in a browser extension, or as a mobile app. If you don't know which one to choose, try the Temple browser extension. Desktop wallets for Tezos include the Temple browser extension, Kukai, and Umami. Mobile apps include Temple, Kukai, and Umami. Switch the wallet to use the Ghostnet testnet instead of Tezos Mainnet. Ghostnet is a network for testing Tezos applications where tokens are free so you don't have to spend real currency to work with your applications. For example, for the Temple browser wallet, click Tezos Mainnet at the top and then click Ghostnet Testnet, as in this picture: Selecting the Ghostnet testnet in the Temple wallet From your wallet, get the address of your account, which starts with tz1. This is the address that applications use to work with your wallet. Go to the Ghostnet faucet page at https://faucet.ghostnet.teztnets.com. On the faucet page, paste your wallet address into the input field labeled "Or fund any address" and click the button for the amount of tez to add to your wallet. 20 tez is enough to work with the tutorial contract, and you can return to the faucet later if you need more tez. It may take a few minutes for the faucet to send the tokens and for those tokens to appear in your wallet. You can use the faucet as much as you need to get tokens on the testnet, but those tokens are worthless and cannot be used on Mainnet. Fund your wallet using the Ghostnet Faucet Now you have an account and funds that you can use to work with Tezos. Creating the contract The contract that you will create has these basic parts: A type that describes the contract's storage, in this case an integer. The storage can be a primitive type such as an integer, string, or timestamp, or a complex data type that contains multiple values. For more information on contract data types, see Data types. Functions called entrypoints that run code when clients call the contract. A type that describes the return value of the entrypoints. Follow these steps to create the code for the contract: Open the LIGO online IDE at https://ide.ligolang.org/. You can work with LIGO code in any IDE, but this online IDE keeps you from having to install software on your computer, and it also simplifies the process of deploying contracts. At the top right of the page, in the Network menu, select Ghostnet, as shown in this picture: Selecting Ghostnet in the list of networks Connect a wallet to the IDE: At the top right of the page, click the Keypair Manager button. In the Keypair Manager window, import the account that you created earlier or create and fund a new account to use with the IDE. To import the account that you created earlier, export the private key from your wallet app, click Import in the Keypair Manager window, and paste the private key. Now you can use your account in the IDE. To create an account to use with the IDE, click Create in the Keypair Manager window, give the new keypair a name, and click Create. Then, copy the address of the keypair and get tez from the faucet as you did in Creating and funding a wallet. In the IDE, create a project from the empty template and select the JsLIGO syntax, as shown in this picture: Creating a project The IDE creates a project and a contract file named Contract.jsligo. In the contract file, create a namespace named Counter to hold the code for the contract: Inside the namespace, create a TypeScript type to set the storage type to an integer: Add this code to define the return type for the entrypoints. Tezos entrypoints return two values: a list of other operations to call and the new value of the contract's storage. Add the code for the increment and decrement entrypoints: These functions begin with the @entry annotation to indicate that they are entrypoints. They accept two parameters: the change in the storage value (an integer) and the current value of the storage (in the storage type that you created earlier in the code). They return a value of the type returnValue that you created in the previous step. Each function returns an empty list of other operations to call and the new value of the storage. Add this code for the reset entrypoint: This function is similar to the others, but it does not take the current value of the storage into account. It always returns an empty list of operations and 0. The complete contract code looks like this: Testing and compiling the contract Before you can deploy the contract to Tezos, you must compile it to Michelson, the base language of Tezos contracts. Set the compiler to target the namespace to compile in your code: On the left side of the page, under Actions, click Project Settings. On the Project Settings tab, in the Module name field, set the module name to Counter. Close the Project Settings tab. Test the contract by passing parameters and the storage value to the LIGO dry-run command: On the left side of the page, under Actions, click Dry Run. In the Dry Run window, select the Increment entrypoint, set the input parameter to 32 and the storage to 10. The Dry Run window looks like this: The Dry Run window, showing the entrypoint to run, the parameter to pass, and the value of the storage Click Run. At the bottom of the window, the Result field shows the response (LIST_EMPTY(), 42). This response means that the contract did not call any other contracts, so the list of operations is empty. Then it shows the new value of the storage. You can test the decrement function in the same way. If you see any errors, make sure that the code of your contract matches the code in the previous section. Test the Reset entrypoint in the same way, but pass unit as the input parameter and any integer in the storage field. The Reset entrypoint takes no parameters, but technically it accepts the value unit, which means no parameter. The Result field shows the response (LIST_EMPTY(), 0), which means that the storage value is now 0. On the left side of the page, under Actions, click Compile, and in the Compile window, click Compile again. If the compilation succeeds, the IDE prints the compiled code to the terminal and saves it to the file build/contracts/Contract.tz. You can see the code by expanding your project on the left side of the page, under .workspaces, and double-clicking Contract.tz. If you see error messages, verify that your contract code matches the code in the previous section. Now you can deploy the contract. Deploying (originating) to the testnet Deploying a contract to the network is called "originating." Originating the contract requires a small amount of Tezos tokens as a fee. On the left side of the page, under Actions, click Deploy. You may see a warning that the initial storage is not set. You can ignore this warning because you can set the initial storage now. In the Deploy contract window, in the Init storage field, set the initial value for the contract's storage to an integer. In the Signer field, make sure your account is selected. Click Estimate. The window shows the estimated fees to deploy the contract, as in this picture: The estimate of the fees to deploy the contract Click Deploy. The deployment process can take a few minutes. When the contract is deployed, the Deploy contract window shows the address at the bottom of the window. Copy the address of the deployed contract, which starts with KT1. :::warning Copy the contract address now, because it will not be shown again. ::: Now you can call the contract from any Tezos client, including web applications and command-line applications like The Octez client. Calling the contract These steps show you how to inspect the contract with a block explorer, which is a web application that shows information about Tezos. It also allows you to call the contract. Open the block explorer Better Call Dev at this link: https://better-call.dev/ Paste the address of the contract in the search field and press Enter. The block explorer shows information about the contract, including recent transactions and the current state of its storage. The block explorer, showing information about the contract Try calling one of the entrypoints: Go to the Storage tab and check the current state of the storage, which should be the integer that you put in the Deploy window. Go to the Interact tab. This tab shows the entrypoints in the contract and lets you use them. For the increment entrypoint, in the Parameters section, put an integer in the field, as shown in this image: Putting in a value for an entrypoint parameter Click Execute and then click Wallet. Select your wallet and connect it to the application. Confirm the transaction in your wallet. Wait for a success message that says "The transaction has successfully been broadcasted to the network." Go back to the Storage tab and see the new value of the storage, as in this picture: Updated storage value Summary Now the contract is running on the Tezos blockchain. You or any other user can call it from any source that can send transactions to Tezos, including Octez, dApps, and other contracts. If you want to continue working with this contract, here are some ideas: Change permissions for the contract so only your account can call its entrypoints Add your own entrypoints and originate a new contract; note that you cannot update the existing contract after it is deployed Create a dApp to call the contract from a web application, similar to the dApp that you create in the tutorial Build a simple web application Deploy a smart contract with CameLIGO This tutorial covers writing and deploying a simple smart contract with the LIGO programming language. Specifically, this tutorial uses the CameLIGO version of LIGO, which has syntax similar to OCaml, but you don't need any experience with OCaml or LIGO to do this tutorial. If you are more familiar with JavaScript, try Deploy a smart contract with JsLIGO. If you are more familiar with Python, try Deploy a smart contract with SmartPy. To learn the Archetype language, try Deploy a smart contract with Archetype. LIGO is a high-level programming language that you can use to write smart contracts for the Tezos blockchain. It abstracts away the complexity of using Michelson (the smart contract language directly available on-chain) to make it easier to write smart contracts on Tezos. In this tutorial, you will learn how to: Create a wallet to store cryptocurrency tokens Get free tez tokens (the native cryptocurrency token on Tezos) from a faucet Code a contract in LIGO, including: Defining the storage for the contract Defining entrypoints in the contract Writing code to run when the entrypoints are called Deploy (or originate) the contract to Tezos and set its starting storage value Look up the current state of the contract Call the contract What is a smart contract? A smart contract is a computer program that is stored on a blockchain and runs on a blockchain. Because the blockchain is spread across many computer nodes, you don't have to think about where to host the program or worry whether a computer will run it or not. Responsibility for running the contract is distributed across all of the nodes in the Tezos system, so when you deploy a smart contract, you can be confident that it will be available and unmodified when someone wants to run it. A smart contract has these parts: Persistent storage, data that the contract can read and write One or more entrypoints, which are a kind of function that clients can call, like endpoints in an API or functions or methods in many programming languages A Tezos account that can store tokens (technically, the contract is itself a type of Tezos account, but you can think of it as a program with a Tezos account) Tutorial contract The contract that you deploy in this tutorial stores a single integer. It provides entrypoints that clients can call to change the value of that integer: The increment entrypoint accepts an integer as a parameter and adds that integer to the value in storage The decrement entrypoint accepts an integer as a parameter and subtracts that integer from the value in storage The reset entrypoint takes no parameters and resets the value in storage to 0 After you deploy the contract, you or any other user can call it from various sources, including web applications, other contracts, and the Octez command-line client. However, no one can prevent it from running or tamper with its code or its storage. Creating and funding a wallet To deploy and work with the contract, you need a wallet and some tez tokens. You can get test tokens for free on the Ghostnet test network: Install a Tezos-compatible wallet. Which wallet you install is up to you and whether you want to install a wallet on your computer, in a browser extension, or as a mobile app. If you don't know which one to choose, try the Temple browser extension. Desktop wallets for Tezos include the Temple browser extension, Kukai, and Umami. Mobile apps include Temple, Kukai, and Umami. Switch the wallet to use the Ghostnet testnet instead of Tezos Mainnet. Ghostnet is a network for testing Tezos applications where tokens are free so you don't have to spend real currency to work with your applications. For example, for the Temple browser wallet, click Tezos Mainnet at the top and then click Ghostnet Testnet, as in this picture: Selecting the Ghostnet testnet in the Temple wallet From your wallet, get the address of your account, which starts with tz1. This is the address that applications use to work with your wallet. Go to the Ghostnet faucet page at https://faucet.ghostnet.teztnets.com. On the faucet page, paste your wallet address into the input field labeled "Or fund any address" and click the button for the amount of tez to add to your wallet. 20 tez is enough to work with the tutorial contract, and you can return to the faucet later if you need more tez. It may take a few minutes for the faucet to send the tokens and for those tokens to appear in your wallet. You can use the faucet as much as you need to get tokens on the testnet, but those tokens are worthless and cannot be used on Mainnet. Fund your wallet using the Ghostnet Faucet Now you have an account and funds that you can use to work with Tezos. Creating the contract The contract that you will create has these basic parts: A type that describes the contract's storage, in this case an integer. The storage can be a primitive type such as an integer, string, or timestamp, or a complex data type that contains multiple values. For more information on contract data types, see Data types. Functions called entrypoints that run code when clients call the contract. A type that describes the return value of the entrypoints. Follow these steps to create the code for the contract: Open the LIGO online IDE at https://ide.ligolang.org/. You can work with LIGO code in any IDE, but this online IDE keeps you from having to install software on your computer, and it also simplifies the process of deploying contracts. At the top right of the page, in the Network menu, select Ghostnet, as shown in this picture: Selecting Ghostnet in the list of networks Connect a wallet to the IDE: At the top right of the page, click the Keypair Manager button. In the Keypair Manager window, import the account that you created earlier or create and fund a new account to use with the IDE. To import the account that you created earlier, export the private key from your wallet app, click Import in the Keypair Manager window, and paste the private key. Now you can use your account in the IDE. To create an account to use with the IDE, click Create in the Keypair Manager window, give the new keypair a name, and click Create. Then, copy the address of the keypair and get tez from the faucet as you did in Creating and funding a wallet. In the IDE, create a project from the empty template and select the CameLIGO syntax, as shown in this picture: Creating a project The IDE creates a project and a contract file named Contract.mligo. In the contract file, create a type to set the storage type to an integer: Add this code to define the return type for the entrypoints. Tezos entrypoints return two values: a list of other operations to call and the new value of the contract's storage. Add the code for the increment and decrement entrypoints: These functions begin with the @entry annotation to indicate that they are entrypoints. They accept two parameters: the change in the storage value (an integer) and the current value of the storage (in the storage type that you created earlier in the code). They return a value of the type returnValue that you created in the previous step. Each function returns an empty list of other operations to call and the new value of the storage. Add this code for the reset entrypoint: This function is similar to the others, but it does not take the current value of the storage into account. It always returns an empty list of operations and 0. The complete contract code looks like this: Testing and compiling the contract Before you can deploy the contract to Tezos, you must compile it to Michelson, the base language of Tezos contracts. Set the compiler to target the module to compile in your code: On the left side of the page, under Actions, click Project Settings. On the Project Settings tab, in the Module name field, set the module name to Counter. Close the Project Settings tab. Test the contract by passing parameters and the storage value to the LIGO dry-run command: On the left side of the page, under Actions, click Dry Run. In the Dry Run window, select the Increment entrypoint, set the input parameter to 32 and the storage to 10. The Dry Run window looks like this: The Dry Run window, showing the entrypoint to run, the parameter to pass, and the value of the storage Click Run. At the bottom of the window, the Result field shows the response (LIST_EMPTY(), 42). This response means that the contract did not call any other contracts, so the list of operations is empty. Then it shows the new value of the storage. You can test the decrement function in the same way. If you see any errors, make sure that the code of your contract matches the code in the previous section. Test the Reset entrypoint in the same way, but pass unit as the input parameter and any integer in the storage field. The Reset entrypoint takes no parameters, but technically it accepts the value unit, which means no parameter. The Result field shows the response (LIST_EMPTY(), 0), which means that the storage value is now 0. On the left side of the page, under Actions, click Compile, and in the Compile window, click Compile again. If the compilation succeeds, the IDE prints the compiled code to the terminal and saves it to the file build/contracts/Contract.tz. You can see the code by expanding your project on the left side of the page, under .workspaces, and double-clicking Contract.tz. If you see error messages, verify that your contract code matches the code in the previous section. Now you can deploy the contract. Deploying (originating) to the testnet Deploying a contract to the network is called "originating." Originating the contract requires a small amount of Tezos tokens as a fee. On the left side of the page, under Actions, click Deploy. You may see a warning that the initial storage is not set. You can ignore this warning because you can set the initial storage now. In the Deploy contract window, in the Init storage field, set the initial value for the contract's storage to an integer. In the Signer field, make sure your account is selected. Click Estimate. The window shows the estimated fees to deploy the contract, as in this picture: The estimate of the fees to deploy the contract Click Deploy. The deployment process can take a few minutes. When the contract is deployed, the Deploy contract window shows the address at the bottom of the window. Copy the address of the deployed contract, which starts with KT1. :::warning Copy the contract address now, because it will not be shown again. ::: Now you can call the contract from any Tezos client, including web applications and command-line applications like The Octez client. Calling the contract These steps show you how to inspect the contract with a block explorer, which is a web application that shows information about Tezos. It also allows you to call the contract. Open the block explorer Better Call Dev at this link: https://better-call.dev/ Paste the address of the contract in the search field and press Enter. The block explorer shows information about the contract, including recent transactions and the current state of its storage. The block explorer, showing information about the contract Try calling one of the entrypoints: Go to the Storage tab and check the current state of the storage, which should be the integer that you put in the Deploy window. Go to the Interact tab. This tab shows the entrypoints in the contract and lets you use them. For the increment entrypoint, in the Parameters section, put an integer in the field, as shown in this image: Putting in a value for an entrypoint parameter Click Execute and then click Wallet. Select your wallet and connect it to the application. Confirm the transaction in your wallet. Wait for a success message that says "The transaction has successfully been broadcasted to the network." Go back to the Storage tab and see the new value of the storage, as in this picture: Updated storage value Summary Now the contract is running on the Tezos blockchain. You or any other user can call it from any source that can send transactions to Tezos, including Octez, dApps, and other contracts. If you want to continue working with this contract, here are some ideas: Change permissions for the contract so only your account can call its entrypoints Add your own entrypoints and originate a new contract; note that you cannot update the existing contract after it is deployed Create a dApp to call the contract from a web application, similar to the dApp that you create in the tutorial Build a simple web application Deploy a smart contract with SmartPy This tutorial covers writing and deploying a simple smart contract with the SmartPy programming language. SmartPy has syntax similar to Python, but you don't need any experience with Python or SmartPy to do this tutorial. If you are more familiar with OCaml, try Deploy a smart contract with CameLIGO. If you are more familiar with JavaScript, try Deploy a smart contract with JsLIGO. To learn the Archetype language, try Deploy a smart contract with Archetype. SmartPy is a high-level programming language that you can use to write smart contracts for the Tezos blockchain. It abstracts away the complexity of using Michelson (the smart contract language directly available on-chain) to make it easier to write smart contracts on Tezos. In this tutorial, you will learn how to: Create a wallet to store cryptocurrency tokens Get free tez tokens (the native cryptocurrency token on Tezos) from a faucet Code a contract in SmartPy, including: Creating a contract in the online IDE Defining the storage for the contract Defining entrypoints in the contract Writing code to run when the entrypoints are called Deploy (or originate) the contract to Tezos and set its starting storage value Look up the current state of the contract Call the contract What is a smart contract? A smart contract is a computer program that is stored on a blockchain and runs on a blockchain. Because the blockchain is spread across many computer nodes, you don't have to think about where to host the program or worry whether a computer will run it or not. Responsibility for running the contract is distributed across all of the nodes in the Tezos system, so when you deploy a smart contract, you can be confident that it will be available and unmodified when someone wants to run it. A smart contract has these parts: Persistent storage, data that the contract can read and write One or more entrypoints, which are a kind of function that clients can call, like endpoints in an API or functions or methods in many programming languages A Tezos account that can store tokens (technically, the contract is itself a type of Tezos account, but you can think of it as a program with a Tezos account) Tutorial contract The contract that you deploy in this tutorial stores a string value. It provides entrypoints that clients can call to change the value of that string: The replace entrypoint accepts a new string as a parameter and stores that string, replacing the existing string. The append entrypoint accepts a new string as a parameter and appends it to the existing string. After you deploy the contract, you or any other user can call it from various sources, including web applications, other contracts, and the Octez command-line client. However, no one can prevent it from running or tamper with its code or its storage. Creating and funding a wallet To deploy and work with the contract, you need a wallet and some tez tokens. Install a Tezos-compatible wallet. Which wallet you install is up to you and whether you want to install a wallet on your computer, in a browser extension, or as a mobile app. If you don't know which one to choose, try the Temple browser extension. Desktop wallets for Tezos include the Temple browser extension, Kukai, and Umami. Mobile apps include Temple, Kukai, and Umami. Switch the wallet to use the Ghostnet testnet instead of Tezos Mainnet. Ghostnet is a network for testing Tezos applications where tokens are free so you don't have to spend real currency to work with your applications. For example, for the Temple browser wallet, click Tezos Mainnet at the top and then click Ghostnet Testnet, as in this picture: Selecting the Ghostnet testnet in the Temple wallet From your wallet, get the address of your account, which starts with tz1. This is the address that applications use to work with your wallet. Go to the Ghostnet faucet page at https://faucet.ghostnet.teztnets.com. On the faucet page, paste your wallet address into the input field labeled "Or fund any address" and click the button for the amount of tez to add to your wallet. 20 tez is enough to work with the tutorial contract, and you can return to the faucet later if you need more tez. It may take a few minutes for the faucet to send the tokens and for those tokens to appear in your wallet. You can use the faucet as much as you need to get tokens on the testnet, but those tokens are worthless and cannot be used on Mainnet. Fund your wallet using the Ghostnet Faucet Now you have an account and funds that you can use to work with Tezos. Creating the contract The contract that you will create has these basic parts: A function that initializes the contract and sets the starting value for its storage. Internal functions called entrypoints that run code when clients call the contract. Automated tests that verify that the contract works as expected. Follow these steps to create the code for the contract: Open the SmartPy online IDE at https://smartpy.io/ide. You can work with SmartPy code in any IDE, but this online IDE keeps you from having to install software on your computer, and it also simplifies the process of deploying contracts. In the code editor, add this line of code to import SmartPy: Add this code that creates the entrypoints: Indentation is significant in Python, so make sure that your indentation matches this code. The first two lines create a SmartPy module, which indicates that the code is SmartPy instead of ordinary Python. Then the code creates a class named StoreGreeting, which represents the smart contract. The contract has an __init__ function, which runs when the contract is deployed. In this case, the function sets the initial value of the storage to a parameter that you pass when you deploy the contract. This storage value is a string, but the storage can be another primitive type such as an integer or timestamp, or a complex data type that contains multiple values. For more information on contract data types, see Data types. Add this code, which creates automated tests: When you run the SmartPy file, SmartPy runs a simulation in which it tests and compiles the contract. In this case, the tests verify that the replace and append endpoints work. For more information about SmartPy and tests, see the SmartPy documentation. The SmartPy online IDE looks like this: The SmartPy online IDE, including the code for the contract The complete contract looks like this: Testing and compiling the contract Before you can deploy the contract to Tezos, you must compile it to Michelson, the base language of Tezos contracts. The compilation process automatically runs the tests. Compile the contract and run the tests by clicking the Run Code button: The right-hand pane of the online IDE shows the results of the simulation, compilation, and testing process. The first step is simulating the deployment (origination) of the contract. The simulation assigns the contract a temporary address and shows the initial state of its storage: The originated contract and the initial storage in the SmartPy IDE Then, the simulation runs the test cases and shows the results of each call to an entrypoint: The results of the entrypoint calls Deploying (originating) to the testnet Deploying a contract to the network is called "originating." Originating the contract requires a small amount of Tezos tokens as a fee. Under the origination step, click Show Michelson. The originated contract, with the Show Michelson button highlighted The IDE shows the compiled Michelson code of the contract, which is the language that smart contracts use on Tezos. Below the Michelson code, click Deploy Contract. In the new window, under "Node and Network," select the Ghostnet testnet and accept the default RPC node, as in this picture: Selecting the Ghostnet network and default RPC node Under "Wallet," click Select Account. In the pop-up window, connect your wallet. For Temple wallets, use the Temple tab, and for most other wallets, use the Beacon tab. When your wallet is connected, click Validate. The Origination page shows your wallet information: The successful connection to your wallet on the origination page At the bottom of the page, click Deploy Contract. In the pop-up window, click Accept. Approve the transaction in your wallet app. The "Origination Result" section shows information about the deployed contract, including its address: Information about the originated contract Under the contract address, click Save Contract. In the popup window, give the contract a name and click Add Contract. Saving the contract address like this is important because the address is not shown again. Open the contract in the block explorer Better Call Dev: In a new browser tab, go to https://better-call.dev/. Paste the contract address in the search box and press Enter. The block explorer shows information about the contract, including recent transactions and the current state of its storage. The block explorer, showing information about the contract Try calling one of the entrypoints: Go to the Storage tab and check the current state of the storage. If you just originated the contract, the storage is "Hello" because that's the value set in the smart contract code. Go to the Interact tab. This tab shows the entrypoints in the contract and lets you use them. For the append entrypoint, in the Parameters section, put some text in the field, as shown in this image: Putting in a value for an entrypoint parameter Click Execute and then click Wallet. Select your wallet and connect it to the application. Confirm the transaction in your wallet. Wait for a success message that says "The transaction has successfully been broadcasted to the network." Go back to the Storage tab and see that the text that you put in the parameter has been added to the contract storage, as in this picture: Updated storage value Summary Now the contract is running on the Tezos blockchain. You or any other user can call it from any source that can send transactions to Tezos, including Octez, dApps, and other contracts. If you want to continue working with this contract, here are some ideas: Change permissions for the contract so only your account can call its entrypoints Add your own entrypoints and originate a new contract; note that you cannot update the existing contract after it is deployed Create a dApp to call the contract from a web application, similar to the dApp that you create in the tutorial Build a simple web application Deploy a smart contract with Archetype This tutorial covers writing a smart contract and deploying it to Tezos in the Archetype programming language. It uses the completium-cli command-line tool, which lets you work with Archetype contracts and Tezos from the command line. If you are more familiar with Python, try Deploy a smart contract with SmartPy. If you are more familiar with OCaml, try Deploy a smart contract with CameLIGO. If you are more familiar with JavaScript, try Deploy a smart contract with JsLIGO. In this tutorial, you will learn how to: Create a wallet to store cryptocurrency tokens Get free tez tokens (the native cryptocurrency token on Tezos) from a faucet Code a contract in Archetype, including: Defining the storage for the contract and its initial value Defining entrypoints in the contract Writing code to run when the entrypoints are called Deploy (or originate) the contract to Tezos Look up the current state of the contract Call the contract from the command line What is a smart contract? A smart contract is a computer program that is stored on a blockchain and runs on a blockchain. Because the blockchain is spread across many computer nodes, you don't have to think about where to host the program or worry whether a computer will run it or not. Responsibility for running the contract is distributed across all of the nodes in the Tezos system, so when you deploy a smart contract, you can be confident that it will be available and unmodified when someone wants to run it. A smart contract has these parts: Persistent storage, data that the contract can read and write One or more entrypoints, which are a kind of function that clients can call, like endpoints in an API or functions or methods in many programming languages A Tezos account that can store tokens (technically, the contract is itself a type of Tezos account, but you can think of it as a program with a Tezos account) The Archetype language Archetype is a high-level language designed specifically for writing Tezos smart contracts. It has features that help you write smart contracts, including: Clear syntax that maps closely with how smart contracts work Enhancements that simplify working with storage Tools that help you verify conditions before running code, such as ensuring that the caller is authorized to run the entrypoint The ability to set up a contract as a state machine, which gives the contract a state and manages transitions between states The ability to verify that the contract does what it says it does through the process of formal verification Like the other languages that Tezos accepts, Archetype code is compiled to Michelson to run on the blockchain. For more information about Archetype, see https://archetype-lang.org/. Tutorial contract The contract that you deploy in this tutorial stores a single integer. It provides entrypoints that clients can call to change the value of that integer: The increment entrypoint accepts an integer as a parameter and adds that integer to the value in storage The decrement entrypoint accepts an integer as a parameter and subtracts that integer from the value in storage The reset entrypoint takes no parameters and resets the value in storage to 0 After you deploy the contract, you or any other user can call it from various sources, including web applications, other contracts, and the Octez command-line client. However, no one can prevent it from running or tamper with its code or its storage. Prerequisites To run this tutorial, you need the completium-cli program: Make sure that NPM is installed by running this command in your command-line terminal: If NPM is not installed, install Node.JS on your computer, which includes NPM, from this link: https://nodejs.org/en. Install completium-cli by running this command: You can verify that completium-cli installed by running this command: If you see a message with the version of completium-cli, it is installed correctly. Initialize completium-cli by running this command: Using a testnet Before you deploy your contract to the main Tezos network (referred to as Mainnet), you can deploy it to a testnet. Testnets are useful for testing Tezos operations because testnets provide tokens for free so you can work with them without spending real tokens. Tezos testnets are listed on this site: https://teztnets.com/. The Ghostnet testnet is a good choice for testing because it is intended to be long-lived, as opposed to shorter-term testnets that allow people to test new Tezos features. By default, completium-cli uses Ghostnet, but these steps verify the network: Verify that completium-cli is set to use Ghostnet by running this command: The response shows the RPC endpoint that completium-cli is using, which is its access point to the Tezos network. If the response shows Current network: ghost, it is using Ghostnet. If completium-cli is not using Ghostnet, switch to Ghostnet by running this command, selecting any endpoint labeled "ghost," and pressing Enter: Creating a local wallet Deploying and using a smart contract costs fees, so you need a local wallet and XTZ tokens. You could use the default accounts that are included in completium-cli, but follow these steps to create your own local wallet on a test network: Run the following command to generate a local wallet, replacing local_wallet with a name for your wallet: Switch to the account that you created by running this command, selecting the new account, and pressing Enter: Get the address for the wallet by running this command: The result shows the address of the account, which begins with "tz1". You need the wallet address to send funds to the wallet, to deploy the contract, and to send transactions to the contract. Copy the address for the account, which is labeled as the "public key hash" in the response to the previous command. The address starts with "tz1". On the testnets page at https://teztnets.com/, click the faucet link for the Ghostnet testnet, which is at https://faucet.ghostnet.teztnets.com. On the faucet page, paste your wallet address into the input field labeled "Or fund any address" and click the button for the amount of XTZ to add to your wallet. 1 XTZ is enough for the tutorial. It may take a few minutes for the faucet to send the tokens and for those tokens to appear in your wallet. You can use the faucet as much as you need to get tokens on the testnet, but those tokens are worthless and cannot be used on Mainnet. Fund your wallet using the Ghostnet Faucet Run this command to check the balance of your wallet: If your wallet is set up correctly and the faucet has sent tokens to it, the response includes the balance of your wallet. Create the contract The contract that you will create has these basic parts: A variable that represents the contract's storage, in this case an integer. Contracts can have storage in the form of primitive types such as an integer, string, or timestamp, or a complex data type that contains multiple values. For more information on contract data types, see Data types. Internal functions called entrypoints that run code when clients call the contract. Follow these steps to create the code for the contract: Run this command to create the contract file: Open the counter.arl file in any text editor. At the top of the file, name the contract by putting the name after the archetype keyword: Define the storage for the contract by adding this line: This line creates a variable in the contract's storage with the name "value." It is an integer type and has the initial value of 10. Any variables that you create with the variable keyword at the top level of the contract become part of its persistent storage. Add the code for the increment and decrement entrypoints: These functions begin with the entry keyword to indicate that they are entrypoints. They accept one parameter: the change in the storage value, which is an integer named delta. One function adds the parameter to the value of the value variable and the other subtracts it. Add this code for the reset entrypoint: This function is similar to the others, but it does not take a parameter. It always sets the value variable to 0. The complete contract code looks like this: Deploying (originating) to the testnet Deploying a contract to the network is called "originating." Originating the contract requires a small amount of Tezos tokens as a fee. Run the following command to originate the smart contract: The command line shows information about the transaction, including the name of the originating account, the target network, and the cost to deploy it. By default, it uses the local alias "Counter" to refer to the contract. Press Y to confirm and deploy the contract. If you see an error that includes the message contract.counter_in_the_past, you waited too long before pressing Y. Run the deploy command again and promptly press Y to confirm it. Print information about the contract by running this command: The response shows information about the contract, including its address on Ghostnet, which starts with "KT1". You can use this information to look up the contract on a block explorer. Verify that the contract deployed successfully by finding it on a block explorer: Open a Tezos block explorer such as TzKT or Better Call Dev. Set the explorer to Ghostnet instead of Mainnet. Paste the contract address, which starts with KT1, into the search field and press Enter. Go to the Storage tab to see that the initial value of the storage is 10. Run this command to see the current value of the contract storage: Calling the contract Now you can call the contract from any Tezos client, including completium-cli. To increment the current storage by a certain value, call the increment entrypoint, as in this example: To decrement the storage, call the decrement entrypoint, as in this example: Finally, to reset the current storage to zero, call the reset entrypoint, as in this example: Then, you can verify the updated storage on the block explorer or by running the completium-cli show storage Counter command. Summary Now the contract is running on the Tezos blockchain. You or any other user can call it from any source that can send transactions to Tezos, including command-line clients, dApps, and other contracts. If you want to continue working with this contract, here are some ideas: Change permissions for the contract so only your account can call its entrypoints Add your own entrypoints and originate a new contract; note that you cannot update the existing contract after it is deployed Create a dApp to call the contract from a web application, similar to the dApp that you create in the tutorial Build a simple web application Create your minimum dapp on Tezos dApp : A decentralized application is a type of distributed open source software application that runs on a peer-to-peer (P2P) blockchain network rather than on a single computer. DApps are visibly similar to other software applications that are supported on a website or mobile device. This tutorial shows you how to create a poke game on smart contract. The game consists on poking the owner of a smart contract. The smart contract keeps a track of user interactions and stores a trace. Poke sequence diagram. You will learn : How to create a Tezos project with Taqueria. How to create a smart contract in JsLIGO. How to deploy the smart contract a real testnet named Ghostnet. How to create a frontend dApp using Taquito library and interact with a Tezos browser wallet. How to use an indexer like TZKT. Prerequisites This tutorial uses Typescript, so it will be easier if you are familiar with JavaScript. Make sure that you have installed these tools: Node.JS and NPM: NPM is required to install the web application's dependencies. Taqueria, version 0.45.0 or later: Taqueria is a platform that makes it easier to develop and test dApps. Docker: Docker is required to run Taqueria. jq: Some commands use the jq program to extract JSON data. yarn: The frontend application uses yarn to build and run (see this article for details about differences between npm and yarn). Any Tezos-compatible wallet that supports Ghostnet, such as Temple wallet. Optionally, you can install VS Code to edit your application code in and the LIGO VS Code extension for LIGO editing features such as code highlighting and completion. Taqueria also provides a Taqueria VS Code extension that helps visualize your project and run tasks. The tutorial application In this tutorial, you create a simple game where the user is poking though a dApp. The user interacts with the smart contract through a web interface, where they can see the current state of the contract and send poke commands to it. The contract responds by updating its storage with the user's address. Alternately, a user can also poke the contract deployed by other users. The application looks like this: Example of the table of addresses and which addresses poked them The code for the completed application is in this GitHub repository: solution When you're ready, move to the next section Create your minimum dApp on Tezos to begin setting up the application. 'Part 1: Create your minimum dApp on Tezos' To start working with the application, you create a Taqueria project and use it to deploy the Poke contract. Then you set up a web application to connect with a wallet, and then interact with your smart contract. Before you begin, make sure that you have installed the tools in the Prerequisites section. Creating a Taqueria project Taqueria manages the project structure and keeps it up to date. For example, when you deploy a new smart contract, Taqueria automatically updates the web app to send transactions to that new smart contract. Follow these steps to set up a Taqueria project: On the command-line terminal, run these commands to set up a Taqueria project and install the LIGO and Taquito plugins: Write the smart contract Edit the pokeGame.jsligo file. Remove the default code and paste this code instead. Every contract has to follow these rules : At least one entrypoint, annotated with @entry , with a mandatory signature taking 2 arguments *(parameter, storage) and a return type. An entrypoint is a function that is exposed as an external API. parameter: the entrypoint parameter. Mandatory and can be of any type. For example: an (ignored) variable starting with_ here, and of type unit (the type void on LIGO). storage: the on-chain storage. Mandatory and can be of any type. For example, here we use the type unit. It is recommended to add an export keyword before the type definition as it is a good practice to export it when you are required to write unit tests from another LIGO file. return_: a mandatory pair of list of operation and the storage type (defined earlier). Return type naming is free but don't use an existing keyword like return. Have a look at the Entrypoints contracts documentation> Note: Previous versions of LIGO used a single main function instead of a function for each entrypoint. This syntax is still valid, but it is harder to read and deprecated in LIGO V1. A Poke variant parameter is generated from the poke entrypoint function under the hood. A variant is more or less equivalent of the Enum type in Javascript. A default main function is generated and act like as a dispatcher for each of your entrypoints. It means that this painful boilerplate is no more needed on the new syntax. Have a look at the Variant type documentation Write the poke function. The objective is to store every user/caller addresses poking the contract. Rewrite the storage, and add the caller address to the set of traces. At line 1, replace the line with: Replace the poke function with: Explanation: The LIGO Set library has a function add to add one element to the Set of items. There is no concept of Class in LIGO, you use a library to apply functions on objects. A list of operations is required to return. An empty list is returned here as there is no other contract to call. Have a look at the Set library documentation Have a look at the List library documentation Here, get the caller address using Tezos.get_source(). Tezos library provides useful functions for manipulating blockchain objects. Have a look at the Tezos library documentation Simulate a call on your smart contract The LIGO command-line provides sub-commands to test your LIGO code. Have a look at the Testing Framework documentation Compile the contract with Taqueria (Force to use a specific LIGO version with TAQ_LIGO_IMAGE Taqueria environment variable). Taqueria is generating the .tz Michelson file in the artifacts folder. The Michelson language is the default stack language used by the Michelson VM to run your code on a node. It is something similar to WASM. Have a look on the Michelson documentation Taqueria is generating two additional files, edit the first file pokeGame.storageList.jsligo replacing the current code with: When you deploy a contract, you are required to initialize the default state of your smart contract. Taqueria allows you to declare different variables on this file, it is useful to use different initialized states per environment. Have a look at the Taqueria documentation Compile all (contract + initial storage) It compiles both source code and storage. Before deployment, to simulate a call to our entrypoint poke, Taq has a taq simulate command. The contract parameter Poke() and the initial storage with the default empty set are passed to the execution. Edit the second file pokeGame.parameterList.jsligo Run the simulation. First, install the Tezos client plugin, recompile it all, and then run the simulation. Output logs: You can notice that the instruction is storing the address of the caller in the storage set. Configure your wallet and deploy The default Tezos testing testnet is called Ghostnet. :warning: You need an account to deploy a contract with some tez (the Tezos native currency). The first time you deploy a contract with Taqueria, it is generating a new user account with 0 tez. Deploy your contract to the testing environment. Ut forces Taqueria to generate a default account on a testing config file. You should get this kind of log. Choice N°1 (Recommended): Use Alice wallet instead of the generated account. A common usage is to use alice account as Taqueria operator. alice is a commonly known address used on Tezos and she has always some tez. Replace the Taqueria config file for testing env .taq/config.local.testing.json with alice settings: Choice N°2: use the Taqueria-generated account. Copy the account privateKey from the .taq/config.local.testing.json config file. Open your Temple browser extension on your computer or on your mobile phone and do the initial setup. Once you are done, go to Settings (click on the avatar icon, or display Temple in full page) and click on Import account > Private key tab. Paste the privateKey to Temple text input and confirm. Send free Tez to your new account via this web faucet here. Connect your wallet on Ghostnet and ask for free tez. Now you have some money to play with. Deploy to Ghostnet testnet. Your smart contract is deployed on the Ghostnet. Create the frontend Create a React app Then follow the prompts. Choose React and then Typescript+SWC: More information about SWC here. Add taquito and tzkt indexer libraries. :warning: Before starting, add the following dependencies in order to resolve polyfill issues. Some dependencies are from NodeJs, thus not included in browsers. For example, in my case, I installed this: Create a new file nodeSpecific.ts in the src folder of your project and edit with this content: Open the index.html file and replace the body with this one: Open the vite.config.ts file and replace it with: Generate the Typescript classes from Michelson code and run the server Taqueria is able to generate Typescript classes for any frontend application. It takes the definition of your smart contract and generates the contract entrypoint functions, type definitions, etc ... To get typescript classes from Taqueria plugin, on your project root folder run: Go back to your frontend app and run the dev server. Open your browser at: http://localhost:5173/ Your app should be running. Connect / disconnect the wallet Declare two React Button components and display the user's address and balance. Edit src/App.tsx file: Let's create the 2 missing src component files: ConnectWallet button creates an instance wallet, gets user permissions via a popup, and then retrieves the current account information. Edit ConnectWallet.tsx Edit DisconnectWallet.tsx The button cleans the wallet instance and all linked objects. Save both files, the dev server should refresh the page. As Temple is configured, click on Connect button. On the popup, select your Temple wallet, then your account, and connect. The app after you have connected, showing your address and tex balance Your are logged. Click on the Disconnect button to test the disconnection, and then reconnect. List other poke contracts via an indexer Instead of querying heavily the RPC node to search where are located all other similar contracts and retrieve each address, use an indexer. an indexer is a kind of enriched cache API on top of an RPC node. In this example, the TZKT indexer is used to find other similar contracts. You need to install jq to parse the Taqueria JSON configuration file. Install jq On package.json, change the dev command on scripts configuration. Prefix it with a jq command to create an new environment variable pointing to your last smart contract address on testing env: The last deployed contract address on Ghostnet is set now on our frontend. Add a button to fetch all similar contracts like yours, then display the list. Edit App.tsx and before the return of App function, add this section for the fetch function. On the returned html template section, after the display of the user balance div I am {userAddress} with {userBalance} mutez, append this: Save your file and restart your server. Now, the start script generates the .env file containing the last deployed contract address. Go to your web browser and click on Fetch contracts button. Congratulations, you are able to list all similar deployed contracts. Poke your contract Import the Taqueria-generated types on app/src/App.tsx. Add this new function after the previous fetch function, it calls the entrypoint for poking. :warning: Normally, a call to c.methods.poke() function is expected by convention, but with an unique entrypoint, Michelson generates a unique default entrypoint name instead of having the name of the entrypoint function. Also, be careful because all entrypoints function names are in lowercase, and all parameter types are in uppercase. Replace the line displaying the contract address {contracts.map((contract) =>
{contract.address}
)} with the one below, it adds a Poke button. Save and see the page refreshed, then click on the Poke button. It calls the contract and adds your public address tz1... to the set of traces. Display poke guys To verify that on the page, we can display the list of poke people directly on the page Replace again the html previous line {contracts ...} with this one Contracts are displaying their people now :information_source: Wait around few second for blockchain confirmation and click on fetch contracts to refresh the list :confetti_ball: Congratulations, you have completed this first dapp training Summary Now, you can create any smart contract using LIGO and create a complete Dapp via Taqueria/Taquito. In the next section, you will learn how to call a Smart contract from a smart contract using callbacks and also write unit and mutation tests. When you are ready, continue to Part 2: Inter-contract calls and testing. 'Part 2: Inter-contract calls and testing' Previously, you learned how to create your first dApp. In this second session, you will enhance your skills on: How to do inter-contract calls. How to use views. How to do unit & mutation tests. On the first version of the Poke game, you were able to poke any deployed contract. Now, you will add a new function to store on the trace an additional feedback message coming from another contract. Poke and Get Feedback sequence diagram Get the code Get the code from the first session: https://github.com/marigold-dev/training-dapp-1/blob/main/solution Reuse the code from the previous smart contract: https://github.com/marigold-dev/training-dapp-1/blob/main/solution/contracts/pokeGame.jsligo Install all libraries locally: Modify the poke function Change the storage to reflect the changes: If you poke directly, you must register the contract's owner's address and no feedback. If you poke and ask to get feedback from another contract, then you register the other contract address and an additional feedback message. Here is the new sequence diagram of the poke function. Edit ./contracts/pokeGame.jsligo and replace the storage definition with this one: Replace your poke function with these lines: Explanation: ...store do a copy by value of your object. Have a look on the Functional updates documentation. Note: you cannot do an assignment like this store.pokeTraces=... in jsLIGO, there are no concepts of Classes, use Functional updates instead. Map.add(...: Add a key, value entry to a map. For more information about Map. export type storage = {...}; a Record type is declared, it is an object structure. Tezos.get_self_address() is a native function that returns the current contract address running this code. Have a look at Tezos native functions. feedback: "": poking directly does not store feedback. Edit pokeGame.storageList.jsligo to change the storage initialization. Compile your contract. Write a second function pokeAndGetFeedback involving the call to another contract a bit later, let's do unit testing first! Write unit tests Add a new unit test smart-contract file unit_pokeGame.jsligo. :information_source: Testing documentation can be found here Edit the file. Explanations: \#import "./pokeGame.jsligo" "PokeGame" to import the source file as a module to call functions and use object definitions. export type main_fn it will be useful later for the mutation tests to point to the main function to call/mutate. Test.reset_state ( 2... this creates two implicit accounts on the test environment. Test.nth_bootstrap_account This returns the nth account from the environment. Test.to_contract(taddr) and Tezos.address(contr) are util functions to convert typed addresses, contract, and contract addresses. let _testPoke = (s : address) : unit => {...} declaring function starting with _ is escaping the test for execution. Use this to factorize tests changing only the parameters of the function for different scenarios. Test.set_source do not forget to set this value for the transaction signer. Test.transfer_to_contract(CONTRACT, PARAMS, TEZ_COST) A transaction to send, it returns an operation. Test.get_storage This is how to retrieve the contract's storage. assert_with_error(CONDITION,MESSAGE) Use assertion for unit testing. const testSender1Poke = ... This test function will be part of the execution report. Test.originate_module(MODULE_CONVERTED_TO_CONTRACT,INIT_STORAGE, INIT_BALANCE) It originates a smart contract into the Test environment. A module is converted to a smart contract. Run the test The output should give you intermediary logs and finally the test results. Do an inter-contract call To keep things simple, 2 versions of the same smart contract are deployed to simulate inter-contract calls and get the feedback message (cf. sequence diagram). Create a new poke function PokeAndGetFeedback: (other : address) with a second part function PokeAndGetFeedbackCallback: (feedback : returned_feedback) as a callback. Calling a contract is asynchronous, this is the reason it is done two times. The function to call on the second contract is GetFeedback: (contract_callback: oracle_param) and returns a feedback message. Very often, this kind of contract is named an Oracle, because generally its storage is updated by an offchain scheduler and it exposes data to any onchain smart contracts. Edit the file pokeGame.jsligo, to define new types: Explanations : type returned_feedback = [address, string] the parameters of an oracle function always start with the address of the contract caller and followed by the return objects. type oracle_param = contract the oracle parameters need to be wrapped inside a typed contract. Write the missing functions, starting with getFeedback. Add this new function at the end of the file. Tezos.transaction(RETURNED_PARAMS,TEZ_COST,CALLBACK_CONTRACT) the oracle function requires to return the value back to the contract caller that is passed already as first parameter. return [list([op]) ,store] this time, you return a list of operations to execute, there is no need to update the contract storage (but it is a mandatory return object). Add now, the first part of the function pokeAndGetFeedback. Tezos.get_entrypoint_opt("%getFeedback",oracleAddress) you require to get the oracle contract address. Then you want to call a specific entrypoint of this contract. The function name is always starting with % with always the first letter in lowercase (even if the code is different). Tezos.transaction(((Tezos.self("%pokeAndGetFeedbackCallback") as contract)),TEZ_COST,call_to_oracle()) The transaction takes as first param the entrypoint of for the callback that the oracle uses to answer the feedback, the tez cost and the oracle contract you got just above as transaction destination. Write the last missing function pokeAndGetFeedbackCallback, receive the feedback and finally store it. let feedbackMessage = {receiver : feedback[0] ,feedback: feedback[1]} prepares the trace including the feedback message and the feedback contract creator. {...store,pokeTraces : Map.add(Tezos.get_source(), feedbackMessage , store.pokeTraces) } add the new trace to the global trace map. Compile the contract. (Optional) Write a unit test for this new function pokeAndGetFeedback. Use views instead of inter-contract call As you saw in the previous step, inter-contract calls make the business logic more complex but not only that, thinking about the cost is even worse. In this training, the oracle is providing a read-only storage that can be replaced by a view instead of a complex and costly callback. See the documentation here about onchain views. :warning: Comment below functions (with /* */ syntax or // syntax) or just remove it, it is no more useful :warning: pokeAndGetFeedbackCallback getFeedback Edit function pokeAndGetFeedback to call view instead of a transaction. Declare the view at the end of the file. Do not forget the annotation @view ! Compile the contract. (Optional) Write a unit test for the updated function pokeAndGetFeedback. Write mutation tests LIGO provides mutation testing through the Test library. Mutation tests are like testing your tests to see if your unit test coverage is strong enough. Bugs, or mutants, are automatically inserted into your code. Your tests are run on each mutant. If your tests fail then the mutant is killed. If your tests passed, the mutant survived. The higher the percentage of mutants killed, the more effective your tests are. Example of mutation features for other languages Create a file mutation_pokeGame.jsligo. Edit the file. Explanation: \#import : import your source code that will be mutated and your unit tests. For more information module doc. const _tests = (ta: typed_address, _: michelson_contract, _: int) : unit => {...: you need to provide the test suite that will be run by the framework. Just point to the unit test you want to run. const test_mutation = (() : unit => {: this is the definition of the mutations tests. Test.originate_module_and_mutate_all(CONTRACT_TO_MUTATE, INIT_STORAGE, INIT_TEZ_COST, UNIT_TEST_TO_RUN): This will take the first argument as the source code to mutate and the last argument as unit test suite function to run over. It returns a list of mutations that succeed (if size > 0 then bad test coverage) or an empty list (good, even mutants did not harm your code). Run the test. Output: Invaders are here. What happened? The mutation has altered a part of the code that is not tested, it was not covered, so the unit test passed. For a short fix, tell the Library to ignore this function for mutants. Go to your source file pokeGame.jsligo, and annotate the function pokeAndGetFeedback with @no_mutation. Run again the mutation tests. Output Update the frontend Reuse the dApp files from the previous session. Redeploy a new version of the smart contract. Note: You can set feedback value to any action other than default kiss string (it is more fun for other to discover it). Adapt the frontend application code. Edit App.tsx, and add new import. Add a new React variable after userBalance definition. Change the poke function to set entrypoint to pokeAndGetFeedback. Change the display to a table changing contracts.map... by: Relaunch the app. On the listed contract, choose your line and input the address of the contract you will receive feedback. Click on poke. The dApp page showing the result of the poke action. This time, the logged user will receive feedback from a targeted contract (as input of the form) via any listed contract (the first column of the table). Refresh manually by clicking on Fetch contracts\` button. Poke other developers' contracts to discover their contract hidden feedback when you poke them. Summary Now, you can call other contracts, use views, and test your smart contract before deploying it. In the next training, you will learn how to use tickets. When you are ready, continue to Part 3: Tickets. 'Part 3: Tickets' Previously, you learned how to do inter-contract calls, use views, and do unit testing. In this third session, you will enhance your skills on: Using tickets. Don't mess up with DUP errors while manipulating tickets. On the second version of the poke game, you were able to poke any contract without constraint. A right to poke via tickets is now mandatory. Tickets are a kind of object that cannot be copied and can hold some trustable information. new Poke sequence diagram Prerequisites Prerequisites are the same as the first session: https://github.com/marigold-dev/training-dapp-1#memo-prerequisites. Get the code from the session 2 solution here. Tickets Tickets came with a Tezos Edo upgrade, they are great and often misunderstood. Ticket structure: Ticketer: (address) the creator contract address. Value: (any) Can be any type from string to bytes. It holds whatever arbitrary values. Amount: (nat) quantity of tickets minted. Tickets features: Not comparable: it makes no sense to compare tickets because tickets of the same type are all equal and can be merged into a single ticket. When ticket types are different then it is no more comparable. Transferable: you can send a ticket into a Transaction parameter. Storable: only on smart contract storage for the moment (Note: a new protocol release will enable it for use accounts soon). Non-dupable: you cannot copy or duplicate a ticket, it is a unique singleton object living in a specific blockchain instance. Splittable: if the amount is > 2 then you can split the ticket object into 2 objects. Mergeable: you can merge tickets from the same ticketer and the same type. Mintable/burnable: anyone can create and destroy tickets. Example of usage: Authentication and Authorization token: giving a ticket to a user provides you with Authentication. Adding some claims/rules on the ticket provides you with some rights. Simplified FA1.2/FA2 token: representing crypto token with tickets (mint/burn/split/join), but it does not have all the same properties and does not respect the TZIP standard. Voting rights: giving 1 ticket that counts for 1 vote on each member. Wrapped crypto: holding XTZ collateral against a ticket, and redeeming it later. Many others ... Minting Minting is the action of creating a ticket from the void. In general, minting operations are done by administrators of smart contracts or either by an end user. Edit the ./contracts/pokeGame.jsligo file and add a map of ticket ownership to the default storage type. This map keeps a list of consumable tickets for each authorized user. It is used as a burnable right to poke. To fill this map, add a new administration endpoint. A new entrypoint Init is adding x tickets to a specific user. Note: to simplify, there is no security around this entrypoint, but in Production it should. Tickets are very special objects that cannot be DUPLICATED. During compilation to Michelson, using a variable twice, copying a structure holding tickets generates DUP command. To avoid our contract failing at runtime, LIGO parses statically our code during compilation time to detect any DUP on tickets. To solve most of the issues, segregate ticket objects from the rest of the storage, or structures containing ticket objects to avoid compilation errors. To do this, just destructure any object until you get tickets isolated. For each function having a storage as parameter, store object needs to be destructured to isolate ticketOwnership object holding our tickets. Then, don't use anymore the store object or it creates a DUP error. Add the new Init function. The Init function looks at how many tickets to create from the current caller, and then it is added to the current map. Modify the poke function. First, extract an existing optional ticket from the map. If an operation is done directly on the map, even trying to find or get this object in the structure, a DUP Michelson instruction is generated. Use the secure get_and_update function from the Map library to extract the item from the map and avoid any copy. Note: more information about this function here. In a second step, look at the optional ticket, if it exists, then burn it (destroy it) and add a trace of execution, otherwise fail with an error message. Same for pokeAndGetFeedback function, do the same checks and type modifications as below. Update the storage initialization on pokeGame.storageList.jsligo. Compile the contract to check for any errors. Note: don't forget to check that Docker is running for taqueria. Check on logs that everything is fine. Try to display a DUP error now. Add this line on poke function just after the first line of storage destructuration const { pokeTraces, feedback, ticketOwnership } = store;. Compile again. This time you should see the DUP warning generated by the find function. Remove it. Test your code Update the unit test files to see if you can still poke. Edit the ./contracts/unit_pokeGame.jsligo file. On Init([sender1, ticketCount]), initialize the smart contract with some tickets. On Fail, check if you have an error on the test (i.e. the user should be allowed to poke). On testSender1Poke, test with the first user using a preexisting ticket. On testSender1PokeWithNoTicketsToFail, test with the same user again but with no ticket, and an error should be caught. Run the test, and look at the logs to track execution. The first test should be fine. Redeploy the smart contract. Let's play with the CLI to compile and deploy. Adapt the frontend code Rerun the app and check that you can not use the app anymore without tickets. Connect with any wallet with enough tez, and Poke your contract. pokefail The Kukai wallet is giving me back the error from the smart contract. kukaifail Ok, so let's authorize some minting on my user and try again to poke. Add a new button for minting on a specific contract, and replace the full content of App.tsx with: Note: You maybe have noticed, but the full typed generated Taquito class is used for the storage access now. It improves maintenance in case you contract storage has changed. Refresh the page, now that you have the Mint button. Mint a ticket on this contract. mint Wait for the Tx popup confirmation and then try to poke again, it should succeed now. success Wait for the Tx popup confirmation and try to poke again, you should be out of tickets and it should fail. kukaifail Congratulations, you know how to use tickets and avoid DUP errors. Takeaways: You can go further and improve the code like consuming one 1 ticket quantity at a time and manage it the right way. You can also implement different type of Authorization mechanism, not only can poke claim. You can also try to base your ticket on some duration time like JSON token can do, not using the data field as a string but as bytes and store a timestamp on it. Summary Now, you understand tickets. If you want to learn more about tickets, read this great article here. In the next training, you will learn how to upgrade smart contracts. When you are ready, continue to Part 4: Smart contract upgrades. 'Part 4: Smart contract upgrades' Upgradable Poke game Previously, you learned how to use tickets and don't mess up with it. In this third session, you will enhance your skills on: Upgrading a smart contract with lambda function code. Upgrading a smart contract with proxy. As you may know, smart contracts are immutable but in real life, applications are not and evolve. During the past several years, bugs and vulnerabilities in smart contracts caused millions of dollars to get stolen or lost forever. Such cases may even require manual intervention in blockchain operation to recover the funds. Let's see some tricks that allow you to upgrade a contract. Prerequisites There is nothing more than you need on the first session: https://github.com/marigold-dev/training-dapp-1#memo-prerequisites. Get the code from the session 3 or the solution here. Upgrades As everyone knows, one feature of blockchain is to keep immutable code on a block. This allows transparency, traceability, and trustlessness. But the application lifecycle implies evolving and upgrading code to fix bugs or bring functionalities. So, how to do it? https://gitlab.com/tezos/tzip/-/blob/master/proposals/tzip-18/tzip-18.md Note: All below solutions break in a wait the fact that a smart contract is immutable. Trust preservation can be safe enough if the upgrade process has some security and authenticity around it. Like the first time an admin deploys a smart contract, any user should be able to trust the code reading it with free read access, the same should apply to the upgrade process (notification of new code version, admin identification, whitelisted auditor reports, ...). To resume, if you really want to avoid DEVOPS centralization, you are about to create a DAO with a voting process among some selected users/administrators in order to deploy the new version of the smart contract ... but let's simplify and talk here only about classical centralized admin deployment. Naive approach One can deploy a new version of the smart contract and do a redirection to the new address on the front end side. Complete flow. | Pros | Cons | | ------------- | ---------------------------------------------------------------------------------------------- | | Easiest to do | Old contract remains active, so do bugs. Need to really get rid off it | | | Need to migrate old storage, can cost a lot of money or even be too big to copy at init time | | | Need to sync/update frontend at each backend migration | | | Lose reference to previous contract address, can lead to issues with other dependent contracts | Stored Lambda function This time, the code will be on the storage and being executed at runtime. Init. Interaction. Administration. Pros/Cons | Pros | Cons | | -------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ | | No more migration of code and storage. Update the lambda function code that is on existing storage | For the storage, all has to be stores as bytes PACKING/UNPACKING it so type checking is lost | | keep same contract address | IDE or tools do not work anymore on lambda code. Michelson does not protect us from some kinds of mistakes anymore | | | Unexpected changes can cause other contract callers to fail, Interface benefits is lost | | | Harder to audit and trace, can lead to really big security nd Trust issues | | | Storing everything as bytes is limited to PACK-able types like nat, string, list, set, map | Implementation Change the implementation of the function pokeAndGetFeedback. The feedback is now a lambda function on the storage. It is required to: Add a new entrypoint to change the lambda code. Update the current entrypoint for calling the lambda. Let's start with adding the lambda function definition of the storage. Let's make minor changes as you have 1 additional field feedbackFunction on storage destructuring. Edit the PokeAndGetFeedback function where the lambda feedbackFunction(..) is executed Notice the line with feedbackFunction(oracleAddress) and call the lambda with the address parameter. The first time, the current code is injected to check that it still works, and then, modify the lambda code on the storage. To modify the lambda function code, add an extra admin entrypoint updateFeedbackFunction. The storage definition is broken, fix all storage missing field warnings on poke and init functions. Change the initial storage with the old initial value of the lambda function (i.e. calling a view to get feedback). Compile and play with the CLI. Redeploy to testnet Test the dApp frontend. Regenerate types and run the frontend. Run the user sequence on the web page: Mint 1 ticket. wait for confirmation. poke a contract address. wait for confirmation. click on the button to refresh the contract list. So far so good, you have the same result as the previous training. Update the lambda function in the background with the CLI through the new admin entrypoint. Return a fixed string this time, just for demo purposes, and verify that the lambda executed is returning another output. Edit the file pokeGame.parameterList.jsligo. Compile all and call an init transaction. Run the user sequence on the web page: Mint 1 ticket. Wait for confirmation. Poke a contract address. Wait for confirmation. Click on the button to refresh the contract list. You see that the feedback has changed to YEAH!!!. Optional: fix the unit tests. Proxy pattern The goal is to have a proxy contract maintaining the application lifecycle, it is an enhancement of the previous naive solution. Deploy a completely new smart contract, but this time, the end user is not interacting directly with this contract. Instead, the proxy becomes the default entrypoint and keeps the same facing address. Init Interaction Administration Note: 2 location choices for the smart contract storage: At proxy level: storage stays unique and immutable. At end-contract level: storage is new at each new version and need to be migrated. Pros/Cons | Pros | Cons | | ------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | | Migration is transparent for frontend | smart contract code Tezos.SENDER always refers to the proxy, so you need to be careful | | if the storage is unchanged, keep the storage at proxy level without cost | If storage changes, need to migrate storage from old contract to new contract and it costs money and having storage at proxy level is not more possible | | keep same contract address | If a contract interface changed, then re-originate the proxy | | | No all of types are compatible with PACKING/UNPACKING, and type checking is lost | | | IDE or tools do not work anymore on lambda code. Michelson does not protect us from some kinds of mistakes anymore | | | Unexpected changes can cause other contract callers to fail, Interface benefits are lost | | | Harder to audit and trace, can lead to really big security nd Trust issues | | | Storing everything as bytes is limited to PACK-able types like nat, string, list, set, map | Implementation Rewrite the smart contract to make it generic Rename the file pokeGame.jsligo to pokeGameLambda.jsligo , as you can have a look on it later. Remove pokeGame.parameterList.jsligo. Get back the original version of pokeGame.jsligo from previous training as it is easier to start from here. Create a new file tzip18.jsligo. Edit the file. This type is included on all smart contract storages to track the proxy address and the last contract version. It is used to block old smart contract instances to be called and check who can call who. Get back to pokeGame.jsligo and import this file on the first line. Add the type to the storage definition. Fix all missing tzip18 fields on the storage structure in the file. The view call signature is different: It returns optional bytes. Calling getView generic view exposed by the proxy. Passing the view named feedback (to dispatch to the correct function once you reach the code that will be executed). Finally, unpack the bytes result and cast it to string. With generic calls, a unique dispatch function has to be used and not multiple @entry. Write a main function annotated with @entry. The parameter is a string representing the entrypoint name and some generic bytes that are required to be cast later on. In a way, compiler checks are broken, so the code is to be well-written and well-cast as earliest as possible to mitigate risks. Start checking that only the proxy contract or the parent of this contract can call the main function. Enable this feature in case the future contract wants to run a migration script itself, reading from children's storage (looking at tzip18.contractPrevious field ). With no more variants, the pattern matching is broken, and if...else statement has to be used instead. When a payload is passed, unpack it and cast it with (Bytes.unpack(action.payload) as option). It means the caller and callee agree on the payload structure for each endpoint. Add the last missing function to change the version of this contract and make it obsolete (just before the main function). Change the view to a generic one and do an if...else on viewName argument. Change the initial storage. Note: for the moment, initialize the proxy address to a fake KT1 address because the proxy is not yet deployed. Compile. All good. Write the unique proxy Create a file proxy.jsligo. Define the storage and entrypoints on it. The storage: Holds a /or several admins. Maintains the interface schema map for all underlying entrypoints. Note on parameters: use @entry syntax, parameters is 2 functions. call: forward any request to the right underlying entrypoint. upgrade: admin endpoint to update the interface schema map or change smart contract version. Add our missing types just above. callContract: payload from user executing an entrypoint (name+payloadBytes) entrypointType: payload to be able to call an underlying contract (name+address) entrypointOperation: change the entrypoint interface map (new state of the map) changeVersion: change the smart contract version (old/new addresses) Add the Callentrypoint (simple forward). (Before the main function). It gets the entrypoint to call and the payload in bytes and just forwards it to the right location. Then, write the upgrade entrypoint. (Before the main function). It loops over the new interface schema to update and do so. If a changeVersion is required, it calls the old contract to take the new version configuration (and it disables itself). The last change is to expose any view from the underlying contract and declare it at the end of the file. Expose a generic view on the proxy and pass the name of the final function called on the underlying contract (as the smart contract view is not unreachable/hidden by the proxy contract). Search for an exposed view on the interface schema to retrieve the contract address, then call the view and return the result as an exposed view. Compile. Deployment Edit proxy.storageList.jsligo to this below ( !!! be careful to point the governance address to your taq default user account !!!). Compile and deploy it. Keep this proxy address, as you need to report it below on tzip18.proxy field. Deploy a smart contract V1. ( :warning: Change with the proxy address on the file pokeGame.storageList.jsligo like here below ). Deploy the underlying V1 contract. Tell the proxy that there is a first contract deployed with some interface. Edit the parameter file proxy.parameterList.jsligo (:warning: Change with the smart contract address on each command line on addr fields below). Compile & Call it. Output: Update the frontend Go on the frontend side, recompile all, and generate typescript classes. Change the script to extract the proxy address instead of the contract one, edit ./app/package.json, and replace the line of script with: Where you created a new file filter.jq with the below content. Edit ./app/src/App.tsx and change the contract address, display, etc ... The contract address now is pointing to the new proxy address. Merge the proxy and contract storage into ProxyStorage\&ContractStorage type definition. Fetching the contracts is appending the storage of the underlying contract to the proxy storage. The call to expose the entrypoint is altered. As all are generic, now on the proxy side, there are only await c.methods.callContract("my_entrypoint_name",my_packed_payload_bytes).send() calls. Run the frontend locally. Do all the same actions as before through the proxy. Login. Refresh the contract list. Mint 1 ticket. Wait for the confirmation popup. Poke. Wait for the confirmation popup. Refresh the contract list. Deploy a new contract V2 and test it again. Note: Remember that the storage.feedback field cannot change on any deployed smart contract because there is no exposed method to update it. Let's change this value for the new contract instance, and call it hello. Edit pokeGame.storageList.jsligo and add a new variable to it. Don't forget again to change proxy and contractPrevious by our values! Tell the proxy that there are new V2 entrypoints and remove the V1 ones. Add a new parameter variable on proxy.parameterList.jsligo. Don't forget to change the addr values with the new contract address just above. Call the proxy to make the changes. Check the logs. Back to the web app, test the flow again: Refresh the contract list. Mint 1 ticket. Wait for the confirmation popup. Poke. Wait for the confirmation popup. Refresh the contract list. Now, the proxy is calling the contract V2 and should return hello on the traces and no more kiss. Set the old smart contract as obsolete Add a new parameter on proxy.parameterList.jsligo to force the change of version of the old contract (:warning: replace below with your addresses for V1 and V2). Compile. Check logs. Check on an indexer that the V1 storage.tzip18.contractNext is pointing to the next version address V2: old V1 contract storage. This ends the proxy pattern implementation. The old contract is no longer runnable and the proxy is pointing to the last version. Alternative: Composability Managing a monolithic smart contract like a microservice can reduce the problem, on the other side it increases complexity and application lifecycle on the OPS side. That's your tradeoff. Summary Now, you can upgrade deployed contracts. Build a simple web application This tutorial shows you how to create a simple web application that uses Tezos. Specifically, this application will be the user-facing web front end for a bank application that accepts deposits and returns withdrawals of test tokens. You will learn: How to create a web application and import libraries that access Tezos How to connect to a user's wallet How to send a transaction to a smart contract on behalf of a user How to get information from Tezos and show it on a web page Prerequisites This tutorial uses JavaScript, so it will be easier if you are familiar with JavaScript. You do not need any familiarity with any of the libraries in the tutorial, including Taquito, a library that helps developers access Tezos. The tutorial application In this tutorial, you build a web application that allows users to send test tokens to a simulated bank on Tezos and withdraw them later. The application looks like this: Completed bank application, showing information about the user's wallet and buttons to deposit or withdraw tez The application connects to a user's cryptocurrency wallet and shows the balance of that wallet in Tezos's native currency, which is referred to as tez, by the ticker symbol XTZ, or the symbol ꜩ. It provides an input field and slider for the user to select an amount of tez to deposit and a button to send the deposit transaction to Tezos. It also provides a button to withdraw the amount from Tezos. The application is based on JavaScript, so it uses several JS-based tools to build and package the application: Svelte for the JavaScript framework Vite (pronounced like veet) to bundle the application and provide the libraries to the user's browser To access the user's wallet and run transactions on Tezos, the application uses these libraries: Taquito to interact with the Tezos blockchain Beacon to access users' wallets The code for the completed application is in this GitHub repository: https://github.com/trilitech/tutorial-applications/tree/main/bank-tutorial. When you're ready, move to the next section to begin setting up the application. "Part 1: Setting up the application" You can access Tezos through any JavaScript framework. This tutorial uses the Svelte framework, and the following steps show you how to start a Svelte application and add the Tezos-related dependencies. Setting up the app Run these commands to install Svelte with TypeScript and Vite: Install the Tezos-related dependencies: Install the buffer, events, and vite-compatible-readable-stream libraries: Update the vite.config.js file to the following code: This updated file includes these changes to the default Vite configuration: It sets the global object to {} so the application can provide the value for this object in the HTML file It includes the a path to the Beacon SDK It provides polyfills for readable-stream and stream Update the default HTML file index.html to the following code: This updated file sets the global variable to globalThis and adds a buffer object to the window. The Beacon SDK requires this configuration to run in a Vite app. Replace the src/main.js file with this code: Configuring Svelte Svelte files include several different types of code in a single file. The application's files have separate sections for JavaScript, style, and HTML code, as in this example: Svelte components are fully contained, which means that the style and code that you apply inside a component doesn't leak into the other components. Styles and scripts that are shared among components typically go in the src/styles and scripts or src/scripts folders. Follow these steps to set up the src/App.svelte file, which is the main component and container for other Svelte components: Replace the default src/App.svelte file with this code: You will add code to connect to the user's wallet in the next section. "Part 2: Accessing wallets" Accessing the user's wallet is a prerequisite for interacting with the Tezos blockchain. Accessing the wallet allows your app to see the tokens in it and to prompt the user to submit transactions, but it does not give your app direct control over the wallet. Users must still confirm all transactions in their wallet application. Using a wallet application in this way saves you from having to implement payment processing and security in your application. As you see in this section, it takes only a few lines of code to connect to a user's wallet. Creating and funding a wallet To use the application, you need a wallet and some tez tokens. Install a Tezos-compatible wallet. Which wallet you install is up to you and whether you want to install a wallet on your computer, in a browser extension, or as a mobile app. If you don't know which one to choose, try the Temple browser extension, because then you can use it in the same browser that you are using to view the web app. Desktop wallets for Tezos include the Temple browser extension, Kukai, and Umami. Mobile apps include Temple, Kukai, and Umami. Switch the wallet to use the Ghostnet testnet instead of Tezos Mainnet. Ghostnet is a network for testing Tezos applications where tokens are free so you don't have to spend real currency to work with your applications. For example, for the Temple browser wallet, click Tezos Mainnet at the top and then click Ghostnet Testnet, as in this picture: Selecting the Ghostnet testnet in the Temple wallet From your wallet, get the address of your account, which starts with tz1. This is the address that applications use to work with your wallet. Go to the Ghostnet faucet page at https://faucet.ghostnet.teztnets.com. On the faucet page, paste your wallet address into the input field labeled "Or fund any address" and click the button for the amount of tez to add to your wallet. 20 tez is enough to work with the tutorial application, and you can return to the faucet later if you need more tez. It may take a few minutes for the faucet to send the tokens and for those tokens to appear in your wallet. You can use the faucet as much as you need to get tokens on the testnet, but those tokens are worthless and cannot be used on Mainnet. Fund your wallet using the Ghostnet Faucet If you created a new account, initialize the account by sending any amount of tez to any other account. Before the new account can use dApps, it must send at least one transaction to Tezos. This first transaction reveals the public key that proves that transactions came from this account. If your account is new, you can send 1 tez to any other account, including your own account, via your wallet application to reveal the account. Now you have an account and funds that you can use in dApps. Connecting to the user's wallet In this section, you add code to connect to the user's wallet with the Taquito TezosToolkit and Beacon BeaconWallet objects. Taquito accesses Tezos and Beacon accesses wallets. IMPORTANT: however you design your app, it is essential to use a single instance of the BeaconWallet object. It is also highly recommended use a single instance of the TezosToolkit object. Creating multiple instances can cause problems in your app and with Taquito in general. This application keeps these objects in the App.svelte file because this is the only component in the application. If you add more components, you should move these objects to a separate file to maintain a single instance of them. In the src/App.svelte file, add these imports to the