This is a workshop for integrating wallets into the frontend of your DApp in the Aleo ecosystem.
The goal of this workshop is to show how developers can take full advantage of Aleo’s privacy preserving tech without sacrificing end-user experience (UX). You'll learn how to integrate existing wallets in the Aleo ecosystem into a frontend app via the universal wallet adapter.
You'll need to install Node.js v18.0.0+ with the NPM package manager for your chosen operating system. Head over to the Node.js website for more information.
Once installed, clone this repository and run npm install inside the project_template directory. This will install all necessary packages.
Finally, run npm run dev inside the same directory to initialize the development server. You can view the application's frontend at http://localhost:5173.
You'll need to have a browser-based wallet that can access the Aleo Testnet. We recommend:
You'll also need some Testnet tokens to fund your wallet. Head over to INSERT_LINK_HERE to acquire some.
Let's take a look at the application's frontend:
| Mint | Transfer |
|---|---|
![]() |
![]() |
We can see that this application is meant to be a token minting and transfer service. In the backend, it will call a workshop_token.aleo program deployed on the blockchain. This frontend should allow users to connect their browser wallet and intiate transactions that call functions from that program. For now, users can only connect their wallets.
The relevant portions of the repository for this workshop can be found in project_template/src/ directory.
src
├── main.tsx
├── App.tsx
├── App.css
├── custom.d.ts
└── workers
├── AleoWorker.ts
└── worker.ts
This file contains some of the base structure code for wallet integration. You shouldn't need to edit any code in here, but we'll take a look to understand how wallet integration occurs.
First, we use the useMemo hook to define which specific wallet adapters we'd like to support. For this workshop, we'll support all four of the Aleo-compatible wallets.
const wallets = useMemo(
() => [
new LeoWalletAdapter({
appName: 'Wallet Workshop',
}),
new PuzzleWalletAdapter({
programIdPermissions: {
[WalletAdapterNetwork.TestnetBeta]: ['credits.aleo, workshop_token.aleo']
},
appName: 'Wallet Workshop',
appDescription: 'A privacy-focused app for the Wallet Integration Workshop',
appIconUrl: ''
}),
new FoxWalletAdapter({
appName: 'Wallet Workshop',
}),
new SoterWalletAdapter({
appName: 'Wallet Workshop',
})
],
[]
);We then need to wrap the app inside both WalletProvider and WalletModalProvider to ensure the wallet functions properly:
return (
<React.StrictMode>
<WalletProvider
wallets={wallets}
network={WalletAdapterNetwork.TestnetBeta}
decryptPermission={DecryptPermission.OnChainHistory}
programs={['credits.aleo', 'workshop_token.aleo']}
autoConnect
>
<WalletModalProvider>
<App />
</WalletModalProvider>
</WalletProvider>
</React.StrictMode>
);There's a lot more depth to all of the parameters and customization options for the above components. For now, we've only covered the basics, but check out the Leo Wallet docs for more information.
App.tsx contains the majority of the relevant code, including the event handlers you'll need to implement. Take note of some key imports from the wallet adapter in this page:
import { WalletMultiButton } from "@demox-labs/aleo-wallet-adapter-reactui";This is the UI component for the Select Wallet button shown above. You'll likely need this for every app you build if you plan on integrating wallet support.
import { useWallet } from "@demox-labs/aleo-wallet-adapter-react";
const { wallet, publicKey, requestTransaction } = useWallet();This is the custom hook for storing state and interacting with the wallet.
We'll only use a small subset of the features of this hook, but if you'd like the full list, check out the Leo Wallet docs.
import { AleoTransaction} from "@demox-labs/aleo-wallet-adapter-base";This is the custom data type for building Aleo transaction that the wallet can interpret. If we dig into the imported code, we can see that an AleoTransaction object has the following structure:
export interface AleoTransaction {
address: string; // The public-key/address that will initiate the transaction
chainId: string; //The network to broadcast to (testnetbeta, mainnet, etc.)
transitions: [
{
program: string; //The program being called
functionName: string; //The function in the above program to be executed
inputs: any[]; //The inputs to the above function
},
... //Can contain any number of transitions
];
fee: number; //The fee to pay for the transaction
feePrivate: boolean; //Is the fee going to be paid with a private record?
}App.tsx also contains the HTML code for displaying most of the webpage. You shouldn't need to edit that part unless you'd like to change how the structure of how the webpage is rendered.
The CSS formatting for the frontend webpage is defined in App.css. If you'd like to change how certain features are displayed (color, sizing, font, etc.), you can do so in this file. We'd recommend not changing it unless you know what you're doing.
This is an additional Typescript file for supporting different filetypes as imports. You shouldn't need to edit anything in this file.
The workers/ subdirectory contains backend code from that's necessary for executing Aleo programs and transactions in the browser. You shouldn't need to edit any of the code in here. If you're curious to learn about WebWorkers and how browser-based execution works under the hood, check out the Provable SDK.
Your task is to fill in the event handler functions in App.tsx that will enable the Mint and Transfer features of the frontend to work properly.
This event handler should call the mint_public() function in workshop_token.aleo with the proper inputs, form the transaction, then broadcast it using the user's connected wallet.
You'll notice that this function has already been filled in for you. You should review and understand the code to see how inputs are parsed and how transactions are formed and broadcast via the wallet adapter.
A few additional notes:
-
The order of the inputs in a transaction matters! Check the order of the inputs in the corresponding Aleo program to ensure correct processing.
-
Double check the state hooks at the top of
App.tsxto see what information is being stored. -
Make sure that your wallet is set to
Testnet Betain the app for proper connectivity.
This event handler should call the mint_private() function in workshop_token.aleo with the proper inputs, form the transaction, then broadcast it using the user's connected wallet.
Remember to review the provided handleMintPublic() function implementation if you get stuck.
-
Check that the wallet is connected and functioning
-
Parse the input fields
AmountandToken Recordfrom their corresponding state hook variablesToken Recordparsing is already implemented for you
-
Set the
mintPrivateLoading()state hook astrue -
Try to form and broadcast the transaction, catching any errors
-
Finally, set the
mintPrivateLoading()state hook asfalse
This event handler should call the transfer_public() function in workshop_token.aleo with the proper inputs, form the transaction, then broadcast it using the user's connected wallet.
Remember to review the provided handleMintPublic() function implementation if you get stuck.
-
Check that the wallet is connected and functioning
-
Parse the input fields
RecipientandAmountfrom their corresponding state hook variablesRecipientparsing is already implemented for you
-
Set the
transferPublicLoading()state hook astrue -
Try to form and broadcast the transaction, catching any errors
-
Finally, set the
transferPublicLoading()state hook asfalse
This event handler should call the transfer_private() function in workshop_token.aleo with the proper inputs, form the transaction, then broadcast it using the user's connected wallet.
Remember to review the provided handleMintPublic() function implementation if you get stuck.
-
Check that the wallet is connected and functioning
-
Parse the input fields
Recipient,Amount, andToken Recordfrom their corresponding state hook variables -
Set the
transferPrivateLoading()state hook astrue -
Try to form and broadcast the transaction, catching any errors
-
Finally, set the
transferPrivateLoading()state hook asfalse



