Skip to content
Open
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
2 changes: 2 additions & 0 deletions packages/app/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@ VITE_ZERODEV_PAYMASTER_URL=
VITE_ZERODEV_PASSKEY_SERVER_URL=
VITE_APP_NAME=MyApp
VITE_COFHE_RPC_URL=https://sepolia-rollup.arbitrum.io/rpc
VITE_JAW_API_KEY=
VITE_JAW_PAYMASTER_URL=
1 change: 1 addition & 0 deletions packages/app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"@zerodev/permissions": "^5.6.3",
"@zerodev/sdk": "^5.5.7",
"@zerodev/webauthn-key": "^5.5.0",
"@jaw.id/core": "^0.2.8",
"@cofhe/sdk": "^0.5.0",
"tfhe": "0.11.1",
"axios": "^1",
Expand Down
8 changes: 3 additions & 5 deletions packages/app/src/hooks/use-auth.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import { useAuthStore } from '@/stores/auth-store';
import { useWalletStore, type WalletProviderType } from '@/stores/wallet-store';
import { DEFAULT_PROVIDER_TYPE, useWalletStore, type WalletProviderType } from '@/stores/wallet-store';
import { AuthService } from '@/services/AuthService';

const DEFAULT_PROVIDER: WalletProviderType = 'zerodev';

function buildSiweMessage(domain: string, address: string, statement: string, uri: string, nonce: string): string {
const now = new Date().toISOString();
return [
Expand Down Expand Up @@ -41,12 +39,12 @@
const walletDisconnect = useWalletStore((s) => s.disconnect);
const authLogout = useAuthStore((s) => s.logout);

async function login(providerType: WalletProviderType = DEFAULT_PROVIDER) {
async function login(providerType: WalletProviderType = DEFAULT_PROVIDER_TYPE) {
const address = await walletConnect(providerType);
await authenticateWithSiwe(address, providerType);
}

async function register(username: string, providerType: WalletProviderType = DEFAULT_PROVIDER) {
async function register(username: string, providerType: WalletProviderType = DEFAULT_PROVIDER_TYPE) {
const address = await walletRegister(providerType, username);
await authenticateWithSiwe(address, providerType);
}
Expand All @@ -54,7 +52,7 @@
async function logout() {
try {
await AuthService.logout();
} catch {

Check warning on line 55 in packages/app/src/hooks/use-auth.ts

View workflow job for this annotation

GitHub Actions / Lint & Format

Empty block statement
} finally {
authLogout();
await walletDisconnect();
Expand Down
77 changes: 77 additions & 0 deletions packages/app/src/providers/jaw/jaw.provider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import type { IWalletProvider, Call } from '../wallet-provider.interface';
import { Account, type AccountConfig } from '@jaw.id/core';
import type { Address, Hex } from 'viem';
import { WindowHelper } from '@/helpers/WindowHelper';

const DEFAULT_CHAIN_ID = 421614; // Arbitrum Sepolia

function getConfig(): AccountConfig {
const apiKey = import.meta.env.VITE_JAW_API_KEY;
if (!apiKey) throw new Error('VITE_JAW_API_KEY is not set');
const paymasterUrl = import.meta.env.VITE_JAW_PAYMASTER_URL;
return {
chainId: DEFAULT_CHAIN_ID,
apiKey,
...(paymasterUrl ? { paymasterUrl } : {}),
};
}

export class JawProvider implements IWalletProvider {
private account: Account | null = null;

async connect(): Promise<string> {
const config = getConfig();

if (Account.getAuthenticatedAddress(config.apiKey)) {
this.account = await Account.get(config);
return this.account.address;
}

return this.login();
}

async login(): Promise<string> {
const config = getConfig();
await WindowHelper.ensureFocus();
this.account = await Account.import(config);
return this.account.address;
}

async register(username: string): Promise<string> {
const config = getConfig();
await WindowHelper.ensureFocus();
this.account = await Account.create(config, { username });
return this.account.address;
}

async disconnect(): Promise<void> {
const { apiKey } = getConfig();
Account.logout(apiKey);
this.account = null;
}

async signMessage(message: string): Promise<string> {
if (!this.account) throw new Error('Not connected');
await WindowHelper.ensureFocus();
return this.account.signMessage(message);
}

getAddress(): string | null {
return this.account?.address ?? null;
}

isConnected(): boolean {
return this.account !== null;
}

async sendUserOperation(calls: Call[]): Promise<string> {
if (!this.account) throw new Error('Not connected');
return this.account.sendTransaction(
calls.map((c) => ({
to: c.to as Address,
data: (c.data ?? '0x') as Hex,
value: c.value,
})),
);
}
}
9 changes: 6 additions & 3 deletions packages/app/src/stores/wallet-store.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
import { create } from 'zustand';
import type { IWalletProvider } from '@/providers/wallet-provider.interface';
import { ZeroDevProvider } from '@/providers/zerodev/zerodev.provider';
import { JawProvider } from '@/providers/jaw/jaw.provider';

export type WalletProviderType = 'zerodev';
export type WalletProviderType = 'zerodev' | 'jaw';

const DEFAULT_PROVIDER_TYPE: WalletProviderType = 'zerodev';
export const DEFAULT_PROVIDER_TYPE: WalletProviderType = 'zerodev';

function createProvider(
type: WalletProviderType,
): IWalletProvider & { register?: (username: string) => Promise<string> } {
switch (type) {
case 'zerodev':
return new ZeroDevProvider();
case 'jaw':
return new JawProvider();
default: {
const exhaustive: never = type;
throw new Error(`Unsupported wallet provider: ${exhaustive as string}`);
Expand All @@ -21,7 +24,7 @@ function createProvider(

function readPersistedProviderType(): WalletProviderType {
const stored = localStorage.getItem('wallet_provider');
if (stored === 'zerodev') return 'zerodev';
if (stored === 'zerodev' || stored === 'jaw') return stored;
return DEFAULT_PROVIDER_TYPE;
}

Expand Down
Loading
Loading