Usage Example with Token Registry Program
Overview
This guide demonstrates how to use the Universal Wallet Adapter with create-leo-app
and interact with the Token Registry Program.
Setup
Create a new application using create-leo-app
:
npm create leo-app@latest
More information about create-leo-app
can be found in here.
Installation
Install the Universal Wallet Adapter in your project:
npm install --save \
@demox-labs/aleo-wallet-adapter-base \
@demox-labs/aleo-wallet-adapter-react \
@demox-labs/aleo-wallet-adapter-reactui \
aleo-adapters
Wallet Provider
Go to src/main.tsx
and wrap your app with the WalletProvider
and WalletModalProvider
components.
Here is an example of the main.tsx
file on how to configure the wallets you want to use:
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import "./index.css";
import { WalletModalProvider } from "@demox-labs/aleo-wallet-adapter-reactui";
import { WalletProvider } from "@demox-labs/aleo-wallet-adapter-react";
import { DecryptPermission, WalletAdapterNetwork } from "@demox-labs/aleo-wallet-adapter-base";
import { useMemo } from "react";
import {
PuzzleWalletAdapter,
LeoWalletAdapter,
FoxWalletAdapter,
SoterWalletAdapter
} from 'aleo-adapters';
const Root = () => {
const wallets = useMemo(
() => [
new LeoWalletAdapter({
appName: 'Aleo app',
}),
new PuzzleWalletAdapter({
programIdPermissions: {
[WalletAdapterNetwork.TestnetBeta]: ['token_registry.aleo']
},
appName: 'Aleo app',
appDescription: 'A privacy-focused DeFi app',
appIconUrl: ''
}),
new FoxWalletAdapter({
appName: 'Aleo app',
}),
new SoterWalletAdapter({
appName: 'Aleo app',
})
],
[]
);
return (
<React.StrictMode>
<WalletProvider
wallets={wallets}
network={WalletAdapterNetwork.TestnetBeta}
decryptPermission={DecryptPermission.UponRequest}
autoConnect
>
<WalletModalProvider>
<App />
</WalletModalProvider>
</WalletProvider>
</React.StrictMode>
);
};
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
<Root />
);
Wallet Button
Go to src/App.tsx
and add the pre-built WalletMultiButton
component by importing it from @demox-labs/aleo-wallet-adapter-reactui
and its css file. Example:
import { WalletMultiButton } from "@demox-labs/aleo-wallet-adapter-reactui";
import "@demox-labs/aleo-wallet-adapter-reactui/dist/styles.css";
// ... existing code ...
return (
<>
<div style={{ position: 'absolute', top: '20px', right: '20px' }}>
<WalletMultiButton />
</div>
{/* ... existing code ... */}
</>
);
Request Transaction
Go to src/App.tsx
and add the useWallet
hook by importing it from @demox-labs/aleo-wallet-adapter-react
:
import { useWallet } from "@demox-labs/aleo-wallet-adapter-react";
// ... existing code ...
const { requestTransaction } = useWallet();
Then try to execute and broadcast a transaction by calling the requestTransaction
function, below is an example of a transaction that registers a new token:
const result = await requestTransaction(
{
address: publicKey || "",
chainId: "testnetbeta",
transitions: [{
program: "token_registry.aleo",
functionName: "register_token",
inputs: [
"12736872field", // token_name
"1273687u128", // token_symbol
"1273687u128", // token_decimals
"6u8", // token_type
"1000000000u128", // token_supply
"false", // external_authorization_required
"aleo1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3ljyzc" // external_authorization_party
]
}],
fee: 100000, // fees in microcredits
feePrivate: false,
}
);
The returned result
string varies based on which wallet that user used to send the transaction.
User will then able to check the transaction status in the wallet after sending the transaction. It will look something like this on Puzzle Wallet:
Next, we can apply the same logic to mint a private token record from the token registry program.
const result = await requestTransaction(
{
address: publicKey || "",
chainId: "testnetbeta",
transitions: [{
program: "token_registry.aleo",
functionName: "mint_private",
inputs: [
"12736872field", // token_name
receivingAddress, // receiving_address
"1000000u128", // token_amount
"false", // external_authorization_required
"0u32" // authorized_until / doesn't matter if external_authorization_required is false
]
}],
fee: 100000, // fees in microcredits
feePrivate: false,
}
);
Once transaction is finalized on chain, a helper function can be created with requestRecordPlaintexts
from useWallet
hook to request every record from the specific program ID and display only unspent records to users in plaintext:
const { requestRecordPlaintexts } = useWallet();
// ... existing code ...
async function requestRecord() {
if (!requestRecordPlaintexts) {
alert("No wallet connected");
return;
}
const records = await requestRecordPlaintexts('token_registry.aleo');
const unspentRecords = records.filter(record => !record.spent);
if (unspentRecords.length > 0) {
console.log("Unspent Records:");
unspentRecords.forEach((record, index) => {
console.log(`Record ${index + 1}:`, record.plaintext);
});
} else {
console.log("No unspent records found");
}
}
Full Example Code
The full example code for App.tsx is provided below:
import { useState } from "react";
import reactLogo from "./assets/react.svg";
import aleoLogo from "./assets/aleo.svg";
import "./App.css";
import { WalletMultiButton } from "@demox-labs/aleo-wallet-adapter-reactui";
import "@demox-labs/aleo-wallet-adapter-reactui/dist/styles.css";
import { useWallet } from "@demox-labs/aleo-wallet-adapter-react";
function App() {
const [receivingAddress, setReceivingAddress] = useState("");
const [executing, setExecuting] = useState(false);
const { publicKey, requestTransaction, requestRecordPlaintexts } = useWallet();
async function execute() {
setExecuting(true);
if (!requestTransaction) {
alert("No wallet connected");
return;
}
const result = await requestTransaction(
{
address: publicKey || "",
chainId: "testnetbeta",
transitions: [{
program: "token_registry.aleo",
functionName: "register_token",
inputs: [
"12736872field", // token_name
"1273687u128", // token_symbol
"1273687u128", // token_decimals
"6u8", // token_type
"1000000000u128", // token_supply
"false", // external_authorization_required
"aleo1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3ljyzc" // external_authorization_party
]
}],
fee: 100000, // fees in microcredits
feePrivate: false,
}
);
setExecuting(false);
console.log(result);
}
async function mintPrivate() {
if (!requestTransaction) {
alert("No wallet connected");
return;
}
const result = await requestTransaction(
{
address: publicKey || "",
chainId: "testnetbeta",
transitions: [{
program: "token_registry.aleo",
functionName: "mint_private",
inputs: [
"12736872field", // token_name
receivingAddress, // receiving_address
"1000000u128", // token_amount
"false", // external_authorization_required
"0u32" // authorized_until / doesn't matter if external_authorization_required is false
]
}],
fee: 100000, // fees in microcredits
feePrivate: false,
}
);
console.log(result);
}
async function requestRecord() {
if (!requestRecordPlaintexts) {
alert("No wallet connected");
return;
}
const records = await requestRecordPlaintexts('token_registry.aleo');
const unspentRecords = records.filter(record => !record.spent);
if (unspentRecords.length > 0) {
console.log("Unspent Records:");
unspentRecords.forEach((record, index) => {
console.log(`Record ${index + 1}:`, record.plaintext);
});
} else {
console.log("No unspent records found");
}
}
return (
<>
<div style={{ position: 'absolute', top: '20px', right: '20px' }}>
<WalletMultiButton />
</div>
<div>
<a href="https://provable.com" target="_blank">
<img src={aleoLogo} className="logo" alt="Aleo logo" />
</a>
<a href="https://react.dev" target="_blank">
<img src={reactLogo} className="logo react" alt="React logo" />
</a>
</div>
<h1>Aleo + React</h1>
<div className="card">
<p>
<button disabled={executing} onClick={execute}>
{executing
? `Executing...check console for details...`
: `Register token on token_registry.aleo`}
</button>
</p>
<input
type="text"
placeholder="Enter receiving address"
value={receivingAddress}
onChange={(e) => setReceivingAddress(e.target.value)}
className="card"
style={{
padding: '0.6em 1.2em',
borderRadius: '8px',
border: '1px solid transparent',
fontSize: '1em',
fontWeight: '500',
fontFamily: 'inherit',
backgroundColor: '#1a1a1a',
cursor: 'text',
transition: 'border-color 0.25s'
}}
/>
<p>
<button onClick={mintPrivate}>
{`Click to mint private token`}
</button>
</p>
<p>
<button onClick={requestRecord}>
{`Request record`}
</button>
</p>
<p>
Edit <code>src/App.tsx</code> and save to test HMR
</p>
</div>
</>
);
}
export default App;
Conclusion
This guide demonstrates how to integrate the Universal Wallet Adapter with the token_registry.aleo program to perform common token operations. We covered:
- Setting up the wallet adapter providers and configuration
- Connecting a supported wallet (Leo Wallet, Puzzle Wallet, Fox Wallet, or Soter Wallet)
- Registering a new token using the
register_token
function - Minting private tokens to a specified address with
mint_private
- Querying and displaying unspent token records using
requestRecordPlaintexts
The Universal Wallet Adapter provides a consistent interface for interacting with Aleo wallets, making it easy to add wallet functionality to your dApp. The adapter handles all the complexities of wallet connections, transaction signing, and record management, allowing developers to focus on building their application logic.
For detailed API documentation, check out the Universal Wallet Adapter documentation.