Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/app/components/Icons/Base64/Azguard.ts

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion apps/app/components/TrainMenu/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ const Comp = () => {
>
{() => (
<div className="h-full openpicker" id="virtualListContainer">
<Wizard wizardId='menuWizard' className="pb-0!">
<Wizard wizardId='menuWizard' className="pb-4!">
<WizardItem StepName={MenuStep.Menu} inModal>
<MenuList goToStep={handleGoToStep} />
</WizardItem>
Expand Down
2 changes: 1 addition & 1 deletion apps/app/components/Wallet/WalletsList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ const WalletsList: FC<Props> = (props) => {
</button>
{
wallets.length > 0 &&
<div className="flex flex-col justify-start space-y-3">
<div className="flex flex-col justify-start space-y-2">
{
wallets.map((wallet, index) => <WalletItem
key={`${index}${wallet.providerName}`}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,21 @@
import React, { createContext, useContext, useState, useCallback, useEffect, useRef, ReactNode } from "react";
import type { WalletProvider as AztecSDKWalletProvider, PendingConnection } from "@aztec/wallet-sdk/manager";
import { AZTEC_APP_ID, useAztecChainInfo } from "./configs";
import { AZTEC_APP_ID, useAztecChainInfo } from "@/lib/wallets/aztec/configs";

// Use a loose type to avoid version mismatches between @aztec/aztec.js versions.
// The wallet-sdk may bundle a different @aztec/aztec.js version than the app.
// eslint-disable-next-line @typescript-eslint/no-explicit-any
type AztecWallet = any;

export const AZGUARD_PROVIDER_ID = 'azguard';

interface AztecWalletContextType {
wallet: AztecWallet | null;
connected: boolean;
accountAddress: string | null;
discoveredProviders: AztecSDKWalletProvider[];
azguardDetected: boolean;
isDiscovering: boolean;
pendingConnection: PendingConnection | null;
verificationEmojis: string | null;
connect: (providerId: string) => Promise<AztecWallet>;
confirmConnection: () => Promise<void>;
cancelConnection: () => void;
disconnect: () => Promise<void>;
startDiscovery: () => void;
}
Expand All @@ -29,7 +27,6 @@ const EmojiVerificationOverlay: React.FC<{
onConfirm: () => void;
onCancel: () => void;
}> = ({ emojis, onConfirm, onCancel }) => {
// Split emojis into a 3x3 grid (9 emojis)
const emojiChars = [...emojis];
const rows = [
emojiChars.slice(0, 3),
Expand Down Expand Up @@ -83,56 +80,83 @@ const EmojiVerificationOverlay: React.FC<{

export const AztecWalletProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
const [wallet, setWallet] = useState<AztecWallet | null>(null);
const [connected, setConnected] = useState(false);
const [accountAddress, setAccountAddress] = useState<string | null>(null);
const [discoveredProviders, setDiscoveredProviders] = useState<AztecSDKWalletProvider[]>([]);
const [azguardDetected, setAzguardDetected] = useState(false);
const [isDiscovering, setIsDiscovering] = useState(false);
const [pendingConnection, setPendingConnection] = useState<PendingConnection | null>(null);
const [verificationEmojis, setVerificationEmojis] = useState<string | null>(null);

const activeProviderRef = useRef<AztecSDKWalletProvider | null>(null);
const discoveryRef = useRef<{ cancel: () => void } | null>(null);
const isDiscoveringRef = useRef(false);
const pendingResolveRef = useRef<((wallet: AztecWallet) => void) | null>(null);
const pendingRejectRef = useRef<((error: Error) => void) | null>(null);

const chainInfo = useAztecChainInfo();

const resetConnection = useCallback(() => {
setWallet(null);
setAccountAddress(null);
activeProviderRef.current = null;
}, []);

const startDiscovery = useCallback(async () => {
if (typeof window === 'undefined' || isDiscovering) return;
if (typeof window === 'undefined' || isDiscoveringRef.current) return;

try {
isDiscoveringRef.current = true;
setIsDiscovering(true);
setDiscoveredProviders([]);
setAzguardDetected(false);

const { WalletManager } = await import("@aztec/wallet-sdk/manager");
const sdkDiscoveryPromise = (async () => {
const { WalletManager } = await import("@aztec/wallet-sdk/manager");

const manager = WalletManager.configure({
extensions: { enabled: true },
});
const manager = WalletManager.configure({
extensions: { enabled: true },
});

const discovery = manager.getAvailableWallets({
chainInfo: await chainInfo(),
appId: AZTEC_APP_ID,
timeout: 10000,
onWalletDiscovered: (provider) => {
setDiscoveredProviders(prev => {
if (prev.some(p => p.id === provider.id)) return prev;
return [...prev, provider];
});
},
});
const discovery = manager.getAvailableWallets({
chainInfo: await chainInfo(),
appId: AZTEC_APP_ID,
timeout: 10000,
onWalletDiscovered: (provider) => {
setDiscoveredProviders(prev => {
if (prev.some(p => p.id === provider.id)) return prev;
return [...prev, provider];
});
},
});

discoveryRef.current = discovery;
discoveryRef.current = discovery;
await discovery.done;
})();

const azguardDetectionPromise = (async () => {
// Poll for window.azguard since content scripts load asynchronously
const maxWait = 1000;
const interval = 100;
let elapsed = 0;
while (elapsed < maxWait) {
if ((window as any).azguard) {
setAzguardDetected(true);
return;
}
await new Promise(r => setTimeout(r, interval));
elapsed += interval;
}
})();

await discovery.done;
await Promise.allSettled([sdkDiscoveryPromise, azguardDetectionPromise]);
} catch (error) {
console.error("Error during wallet discovery:", error);
} finally {
isDiscoveringRef.current = false;
setIsDiscovering(false);
}
}, [chainInfo, isDiscovering]);
}, [chainInfo]);

// Start discovery on mount (client-side only)
useEffect(() => {
if (typeof window === 'undefined') return;
startDiscovery();
Expand All @@ -143,6 +167,25 @@ export const AztecWalletProvider: React.FC<{ children: ReactNode }> = ({ childre
}, []); // eslint-disable-line react-hooks/exhaustive-deps

const connect = useCallback(async (providerId: string): Promise<AztecWallet> => {
// Azguard path: direct connection, no emoji verification
if (providerId === AZGUARD_PROVIDER_ID) {
const { AztecWallet: AzguardAztecWallet } = await import("@azguardwallet/aztec-wallet");
const azguardWallet = await AzguardAztecWallet.connect();

setWallet(azguardWallet);

const accounts = await azguardWallet.getAccounts();
const address = accounts[0]?.item?.toString() ?? accounts[0]?.toString();
if (address) {
setAccountAddress(address);
}

azguardWallet.onDisconnected.addHandler(() => resetConnection());

return azguardWallet;
}

// SDK path: emoji verification flow
const provider = discoveredProviders.find(p => p.id === providerId);
if (!provider) {
throw new Error(`Wallet provider "${providerId}" not found`);
Expand All @@ -161,38 +204,23 @@ export const AztecWalletProvider: React.FC<{ children: ReactNode }> = ({ childre
pendingResolveRef.current = resolve;
pendingRejectRef.current = reject;
});
}, [discoveredProviders]);
}, [discoveredProviders, resetConnection]);

const confirmConnection = useCallback(async () => {
if (!pendingConnection) return;

try {
const connectedWallet = await pendingConnection.confirm();
setWallet(connectedWallet);
setConnected(true);

const accounts = await connectedWallet.getAccounts();
const address = accounts[0]?.toString();
const address = accounts[0]?.item?.toString() ?? accounts[0]?.toString();
if (address) {
setAccountAddress(address);
}

// Register disconnect handler
if (activeProviderRef.current) {
activeProviderRef.current.onDisconnect(() => {
setWallet(null);
setConnected(false);
setAccountAddress(null);
activeProviderRef.current = null;
if (typeof window !== 'undefined') {
localStorage.removeItem("aztec_wallet_connected");
}
});
}

if (typeof window !== 'undefined') {
localStorage.setItem("aztec_wallet_connected", "true");
localStorage.setItem("aztec_wallet_provider_id", activeProviderRef.current?.id ?? "");
activeProviderRef.current.onDisconnect(() => resetConnection());
}

setPendingConnection(null);
Expand All @@ -209,7 +237,7 @@ export const AztecWalletProvider: React.FC<{ children: ReactNode }> = ({ childre
pendingResolveRef.current = null;
pendingRejectRef.current = null;
}
}, [pendingConnection]);
}, [pendingConnection, resetConnection]);

const cancelConnection = useCallback(() => {
if (pendingConnection) {
Expand All @@ -226,33 +254,24 @@ export const AztecWalletProvider: React.FC<{ children: ReactNode }> = ({ childre
try {
if (activeProviderRef.current) {
await activeProviderRef.current.disconnect();
} else if (wallet && typeof wallet.disconnect === 'function') {
await wallet.disconnect();
}
} catch (error) {
console.error("Error disconnecting:", error);
} finally {
setWallet(null);
setConnected(false);
setAccountAddress(null);
activeProviderRef.current = null;
if (typeof window !== 'undefined') {
localStorage.removeItem("aztec_wallet_connected");
localStorage.removeItem("aztec_wallet_provider_id");
}
resetConnection();
}
}, []);
}, [wallet, resetConnection]);

return (
<AztecWalletContext.Provider value={{
wallet,
connected,
accountAddress,
discoveredProviders,
azguardDetected,
isDiscovering,
pendingConnection,
verificationEmojis,
connect,
confirmConnection,
cancelConnection,
disconnect,
startDiscovery,
}}>
Expand Down
2 changes: 1 addition & 1 deletion apps/app/components/WalletProviders/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import StarknetProvider from "./StarknetProvider";
import { ImtblPassportProvider } from "./ImtblPassportProvider";
import { WalletModalProvider } from "../WalletModal";
import { WalletProvidersProvider } from "../../context/walletHookProviders";
import { AztecWalletProvider } from "../../lib/wallets/aztec/AztecWalletProvider";
import { AztecWalletProvider } from "./AztecWalletProvider";
import { EvmConnectorsProvider } from "@/context/evmConnectorsContext";
import { SecretDerivationProvider } from "@/context/secretDerivationContext";

Expand Down
2 changes: 1 addition & 1 deletion apps/app/lib/wallets/aztec/configs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { useSettingsState } from "../../../context/settings";
import KnownInternalNames from "../../knownIds";

// Default Aztec node URL
const DEFAULT_AZTEC_NODE_URL = "https://devnet.aztec-labs.com";
const DEFAULT_AZTEC_NODE_URL = "https://v4-devnet-2.aztec-labs.com";

// Application ID for wallet SDK discovery
export const AZTEC_APP_ID = "train-protocol";
Expand Down
15 changes: 3 additions & 12 deletions apps/app/lib/wallets/aztec/useAtomicAztec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,8 @@ import { LockDetails, LockStatus } from "../../../Models/phtlc/PHTLC"
import { hexToBytes, bytesToHex } from "./utils"
import formatAmount from "../../formatAmount"
import { TrainContract } from "./Train"
import { useSecretDerivation } from "@/context/secretDerivationContext"
import { BaseAtomicFunctions } from "../utils/atomicTypes"
import { secretToHashlock } from "@train-protocol/sdk"
import { parseUnits } from "viem"

export interface UseAtomicAztecParams {
wallet: any
Expand All @@ -18,20 +17,12 @@ export interface UseAtomicAztecParams {

export default function useAtomicAztec(params: UseAtomicAztecParams): BaseAtomicFunctions {
const { wallet, accountAddress, aztecNodeUrl, sponsorAddress } = params
const { deriveSecret } = useSecretDerivation()

const createHTLC = async (params: CreateHTLCParams) => {
const { nonce: timestamp, hashlock } = params;
if (!wallet) throw new Error("No wallet connected");

const { secret, nonce: timestamp } = await deriveSecret({
wallet: { metadata: { wallet }, providerName: 'aztec' } as any
});

const hashlock = secretToHashlock(secret);

const parsedAmount = BigInt(
Math.round(Math.pow(10, params.sourceAsset.decimals) * Number(params.amount))
);
const parsedAmount = parseUnits(params.amount.toString(), params.sourceAsset.decimals);

const { userLockTransactionBuilder } = await import('./transactionBuilder')

Expand Down
Loading