This project is a simple demo app to showcase how to integrate the Amadeus Wallet browser extension into a modern React application.
Highlights
- Detect the wallet provider and react to
accountsChanged- Connect / disconnect flows using
requestAccounts- Token transfers (sign & submit) with AMA token
- Custom contract calls with arbitrary arguments
- Transaction history and event logging panels
- Configurable API endpoint for local node testing
- Bun (latest version)
- Amadeus Wallet extension installed in your browser
- Optional: Running Amadeus node (defaults to
https://nodes.amadeus.bot/api)
bun install
bun run devOpen the displayed URL (default http://localhost:5173). Make sure the Amadeus Wallet extension is installed and unlocked.
src/
├── components/ # React components
│ ├── ui/ # shadcn/ui components
│ ├── ConnectionPanel.tsx
│ ├── CustomCallForm.tsx
│ ├── EventLog.tsx
│ ├── Hero.tsx
│ ├── NetworkEndpointForm.tsx
│ ├── StatusBanner.tsx
│ ├── TransactionHistory.tsx
│ ├── TransferForm.tsx
│ └── index.ts # Component barrel exports
├── contexts/ # React Context providers
│ ├── AppStateContext.tsx
│ └── WalletContext.tsx
├── hooks/ # Custom React hooks
│ ├── index.ts # Hooks barrel exports
│ ├── useAmadeus.ts # Core wallet integration hook
│ ├── useAppState.ts # App state context hook
│ ├── useCustomCall.ts
│ ├── useEventLog.ts
│ ├── useNetworkEndpoint.ts
│ ├── useStatusBanner.ts
│ ├── useTransactionHistory.ts
│ ├── useTransfer.ts
│ └── useWallet.ts # Wallet context hook
├── lib/ # Utility functions
│ └── utils.ts # General utilities
├── types/ # TypeScript type definitions
│ ├── amadeus.d.ts # Amadeus provider types
│ └── index.ts # Shared types
├── App.tsx # Main application component
└── main.tsx # Application entry point
- Automatically checks for
window.amadeus - Listens for the
amadeus#initializedDOM event for late injection - Uses
provider.isConnected()andprovider.getAccount()on load - Status banner shows real-time connection state
Connect WalletcallsrequestAccounts()and renders the active accountDisconnectclears local state (helpful for resetting the demo between tests)- Reacts to
accountsChangedto update the UI in real time - Connection panel displays active account
- Demonstrates signing and submitting AMA token transfers
- Converts human-readable AMA amounts into atomic units (9 decimals)
- Submits signed payloads to the configured node using raw packed bytes
- Transaction history tracks all signed transactions
- Accepts contract/method/args as JSON and pipes them into
signTransaction - Suitable for experimenting with non-token transactions
- Validates JSON input before submission
- Transaction panel keeps a local list of signed/submitted hashes with status
- Event log captures provider state changes, request lifecycle details, and errors
- Status banner provides real-time feedback on wallet connection state
The wallet injects a provider at window.amadeus. Here's a quick reference of the available methods and events as used in this demo:
if (window.amadeus?.isAmadeus) {
const provider = window.amadeus
// Check whether the wallet is unlocked and ready
const unlocked = await provider.isConnected()
// Read the active account (null if locked)
const currentAccount = await provider.getAccount()
// Request permission to access accounts (opens extension popup)
const accounts = await provider.requestAccounts()
// Sign any contract call and receive the packed transaction bytes
const signed = await provider.signTransaction({
contract: 'Coin',
method: 'transfer',
args: ['recipientBase58', '1000', 'AMA'],
description: 'Transfer 1000 AMA'
})
console.log(signed.txHash, signed.txPacked)
}function onAccountsChanged(accounts: unknown) {
if (Array.isArray(accounts) && accounts.length > 0) {
console.log('Primary account', accounts[0])
}
}
window.amadeus?.on('accountsChanged', onAccountsChanged)
// ...later
window.amadeus?.off('accountsChanged', onAccountsChanged)The React hooks (useAmadeus, useWallet) wrap all of the above, providing a declarative interface for components.
import { useWallet } from '@/hooks'
function MyComponent() {
const { account, connect, disconnect, isConnected } = useWallet()
// Use wallet state and methods
}import { useAppState } from '@/hooks'
function MyComponent() {
const { eventLog, transactionHistory, networkEndpoint } = useAppState()
// Access shared app state
eventLog.addLog('info', 'Something happened')
transactionHistory.addTransaction({ ... })
}import { useTransfer } from '@/hooks'
function TransferComponent() {
const { transfer, isSubmitting, isConnected } = useTransfer()
const handleTransfer = async () => {
await transfer(recipient, amount, description)
}
}Use the Network Endpoint input in the UI to point at a local or staging node. The value is persisted during the session and used for all transaction submissions.
bun run dev– Start the Vite dev serverbun run build– Production build (TypeScript check + Vite build)bun run lint– Run ESLintbun run prettier:check– Check code formattingbun run prettier:format– Format code with Prettierbun run preview– Preview production build
- React 19 – UI framework
- TypeScript – Type safety
- Vite – Build tool and dev server
- Tailwind CSS – Styling
- shadcn/ui – UI component library
- Bun – Package manager and runtime