Taquito
package (to interact with the blockchain) and the BeaconWallet
package (to interact with the user’s wallet). These packages are available from NPM
and you can easily install them with npm i @taquito/taquito @taquito/beacon-wallet
. At the top of the main component of the dapp, we can now import two classes we need for our dapp, the TezosToolkit
from the Taquito
package and the BeaconWallet
from the BeaconWallet
package:Taquito
package, we need the TezosToolkit
to communicate with the Tezos blockchain. From the BeaconWallet
package, we need the BeaconWallet
class to interact with the users’ wallets. In addition to that, we are also going to import different things we will need to set up the dapp:onMount
from the svelte
package allows us to prepare the dapp environment when the users load the dapp, we will set up their wallet and their connection to the blockchainTaquito
package exposes different types and interfaces that we can use with TypeScript to make our code more robust, in this case, ContractAbstraction
(that represents the instance of a contract on the dapp level) and Wallet
(that represents an instance of a wallet)NetworkType
is a useful enum exposed by the @airgap/beacon-wallet
that we will use to connect the wallet.Note: you don’t need to install theBeacon SDK
package yourself, it is part of theBeaconWallet
package and will already be present in yournode_modules
folder.
onMount
that is triggered once when the app is mounted. We will set up the dapp within this function:TezosToolkit
. Because it is a class, we can create an instance of it with the new
keyword and we will save it in the Tezos variable (but you can use whatever variable name you like). The TezosToolkit
expects as a parameter the URL of the RPC node you want to connect to. You can use the one in the example or any other one of your choice. Be careful that the node you choose will decide the network you connect to (here the Florencenet testnet).BeaconWallet
and passing it an object. This object must have at least 1 property called name
with the name of your dapp (that’s the name that will be displayed in the wallet when you will ask users to sign transactions). You should also use the preferredNetwork
property that allows you to use the Kukai wallet during development. This property takes the name of the network you want to connect to as a value and this is when we can use the NetworkType
enum.client
with a method called getActiveAccount
that checks if the users had connected their wallets before. If it is the case, this is what we can do:Tezos.setWalletProvider(wallet)
: this step is necessary to register the newly created wallet and to use it to sign transactions. The argument is an instance of the BeaconWallet
userAddress = activeAccount.address
: we get the user’s address which can be useful as much as in the interface (so the users can know with which address they are connected) as in the code (for example, in the following line to fetch the existing tickets)bigmap
using Taquito
:at
method of the wallet
property on the TezosToolkit instance (here, we store this instance in the ticketer
variable) => await Tezos.wallet.at(contractAddress)
storage
method present on the contract instance => await ticketer.storage()
bigmap
, you can use it as a property on the storage instance and call the get
method on it with the key you are looking for to get its value=> await ticketerStorage.tickets.get(keyToFind)
get
method returns the value associated with the key or undefined
if the key was not found.Note: if the key isundefined
, the node will return a 404 error message. This can be confusing if you keep an eye on your console logs as it looks like an error, but it isn’t.
bigmap
and are associated with a key represented as a pair
with the address of the owner on the left and the ticket type on the right. In order to fetch it with Taquito, you can pass an object to the get
function with 2 properties: “0”
for the left side of the pair and “1”
for the right side of the pair. The call will return an object
because the values in the bigmap
are pairs. On the left side, you have the timestamp of the last time the user bought tickets, on the right side, the actual ticket. These are the 2 values the function returns.bigmap
, we can save them in variables to use them in the interface.userAddress
, so this is the value we can use to show a Connect/Disconnect button:BeaconWallet
:requestPermissions
method on the wallet instance will open the Beacon popup and request the users to choose a wallet and connect to it:network
. The property itself takes another object with a type
property (accepting a value of type NetworkType
, the enum we imported earlier from @airgap/beacon-sdk
) and a rpcUrl
property (with the address of the node you want to connect to). After the user accepts the connection, we can move on and fetch the different information we need.Note: it is a good idea to put the code to connect to a wallet into atry…catch…
statement. This is prone to bugs and unexpected behaviours (like users not giving their permissions or failing connections) and you should handle them.
getPKH
method on the wallet instance:TezosToolkit
instance exposes a method called setWalletProvider
that you just need to call and pass the newly created wallet to in order to set up the signer. Then, you can get the user’s address with await wallet.getPKH()
.Note: forgetting to set the wallet provider is a common reason of theSigner Not Configured
error, along with using the Contract API instead of the Wallet API.
disconnect
function is pretty simple, the wallet
instance exposes the client
instance of the Beacon SDK
as a property on which you can call the destroy
method. We will also reset wallet
and userAddress
to return the interface to its original state:loadingBuy
to true
, which will add the loading
class to it and disable it (in order to prevent multiple clicks). At the same time, we only trigger buyTickets
if the user is not already waiting for the confirmation of a previous buy.Note: the number of tickets to buy (1) is hard-coded here but you can also request it from the users.
get
method available on every bigmap of the storage and pass the key we are looking for, standard
. If you remember from earlier, the call is going to fail with a 404
error code if the key doesn’t exist and it will be caught in the try… catch…
statement before sending the transaction.buy_tickets
that takes 3 parameters: the number of tickets to create, the address to be credited with the tickets and the type of tickets. The number of tickets is passed to the function as ticketAmount
, we will use the address that we stored in userAddress
when the user connected their wallet and the ticket type is just going to be “standard”. Every entrypoint of the contract is available by name as a method on the methods
property of the contract abstraction. In order to send the transaction, you call send
on the return value of the entrypoint function.op
). This object exposes different properties and methods, but the ones you will use the most are the opHash
property that holds the hash of the transaction and the confirmation
method that waits for the transaction to be confirmed. By default, op.confirmation()
waits for 1 confirmation, but you can wait for more by passing the number of confirmations you want as a parameter (for example, op.confirmation(10)
to wait for ten confirmations).ticketerStorage = await ticketer.storage()
. This creates a representation of the whole storage in your dapp and you can now access all the information available in the storage.Note: at this point, the transaction has been confirmed and the updated storage is available. The storage you get afterop.confirmation()
is the new storage.
tickets
bigmap so we just need to use the right key to access the number of tickets owned by the user. In the case of a pair used as a key in a bigmap, you can simply pass an object to the get
method with 2 properties: “0”
for the value in the left field of the pair and “1”
for the value in the right field of the pair. If we get a result, it means the user has tickets and we can update the number of tickets (userTickets
) and their validity (userTicketValidity
). Otherwise, we reset everything to zero.Note: we’ve just updated the user’s tickets so we know there are tickets but it’s always better to handle unexpected cases.
methods
property of the contract abstraction:send
to send the transactionconfirmation
on the operation object to wait for the transaction to be included in a blockstorage
on the ticketer contract abstractionTaquito
, connecting to the users’ wallets using Beacon
, sending transactions and fetching a contract storage. This is all you need to create a simple dapp.