diff --git a/src/pages/WalletPage.tsx b/src/pages/WalletPage.tsx
index 94303886..8a52e2ef 100644
--- a/src/pages/WalletPage.tsx
+++ b/src/pages/WalletPage.tsx
@@ -15,6 +15,7 @@ import { useEvmBalance } from '@/hooks/useEvmBalance';
import { useTokenBalance } from '@/hooks/useTokenBalance';
import { useToon } from '@/contexts/ToonContext';
import { useToast } from '@/hooks/useToast';
+import { DevFaucet } from '@/components/wallet/DevFaucet';
function truncateAddress(address: string): string {
return `${address.slice(0, 6)}...${address.slice(-4)}`;
@@ -36,7 +37,7 @@ export function WalletPage() {
evmAddress,
config.toonTokenAddress || null,
config.toonChainRpcUrl,
- 6, // USDC decimals
+ 18, // MockERC20 decimals (local Anvil devnet)
);
const { toast } = useToast();
@@ -201,6 +202,15 @@ export function WalletPage() {
>
Withdraw
+ {evmAddress && (
+
+ )}
{/* ILP Connection Status + Activity */}
diff --git a/src/test/TestApp.tsx b/src/test/TestApp.tsx
index 9e1b3cf6..43a7d347 100644
--- a/src/test/TestApp.tsx
+++ b/src/test/TestApp.tsx
@@ -94,11 +94,6 @@ export function TestApp({ children }: TestAppProps) {
sidebarOrder: [],
nip85StatsPubkey:
"5f68e85ee174102ca8978eef302129f081f03456c884185d5ec1c1224ab633ea",
- blossomServerMetadata: {
- servers: [],
- updatedAt: 0,
- },
- useAppBlossomServers: true,
faviconUrl: "https://ditto.pub/api/favicon/{hostname}",
linkPreviewUrl: "https://ditto.pub/api/link-preview/{url}",
corsProxy: "https://proxy.shakespeare.diy/?url={href}",
diff --git a/vite.config.ts b/vite.config.ts
index 024066ae..794f4a67 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -27,7 +27,9 @@ function nodeBuiltinsShim(): Plugin {
'crypto', 'node:crypto',
'path', 'node:path',
'util', 'node:util',
- 'events', 'node:events',
+ // 'events' and 'node:events' NOT shimmed — @toon-protocol/connector's BTPClient
+ // extends EventEmitter and needs real .emit()/.on()/.removeListener() methods.
+ // Vite resolves 'events' to the browser 'events' polyfill package instead.
'stream', 'node:stream',
// 'buffer' and 'node:buffer' handled below with real polyfill
'better-sqlite3',
@@ -51,6 +53,7 @@ function nodeBuiltinsShim(): Plugin {
// Wraps the native browser WebSocket with a Node.js-style EventEmitter API
// (.on(), .send(), .close()) so that @toon-protocol/core's queryPeerInfo works.
return `
+import { Buffer } from 'buffer';
class BrowserWebSocket {
constructor(url, protocols) {
this._listeners = {};
@@ -58,7 +61,18 @@ class BrowserWebSocket {
this._ws.binaryType = 'arraybuffer';
this._ws.onopen = (e) => this._emit('open', e);
this._ws.onmessage = (e) => {
- this._emit('message', e.data);
+ // Wrap ALL incoming data in Buffer so BTP parser can use readUInt8/readUInt32BE.
+ // Binary frames arrive as ArrayBuffer (binaryType='arraybuffer').
+ // Text frames arrive as strings even with binaryType='arraybuffer'.
+ let data;
+ if (e.data instanceof ArrayBuffer) {
+ data = Buffer.from(e.data);
+ } else if (typeof e.data === 'string') {
+ data = Buffer.from(e.data, 'utf8');
+ } else {
+ data = e.data;
+ }
+ this._emit('message', data);
};
this._ws.onerror = (e) => this._emit('error', e);
this._ws.onclose = (e) => this._emit('close', e.code, e.reason);
@@ -75,7 +89,11 @@ class BrowserWebSocket {
this._listeners[event] = (this._listeners[event] || []).filter(l => l !== fn);
return this;
}
+ removeListener(event, fn) { return this.off(event, fn); }
+ removeAllListeners(event) { if (event) { delete this._listeners[event]; } else { this._listeners = {}; } return this; }
+ emit(event, ...args) { this._emit(event, ...args); return (this._listeners[event] || []).length > 0; }
send(data) { this._ws.send(data); }
+ ping() { /* browser WebSocket has no ping — keep-alive is handled by the protocol */ }
close(code, reason) { this._ws.close(code, reason); }
get readyState() { return this._ws.readyState; }
static get CONNECTING() { return 0; }