Skip to content

Commit 418eb27

Browse files
committed
clean up
1 parent 078ef2c commit 418eb27

File tree

3 files changed

+67
-166
lines changed

3 files changed

+67
-166
lines changed

src/App.tsx

Lines changed: 35 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,15 @@ import "./styles/App.css";
22

33
import { Porto } from "porto";
44
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
5-
import { type Address, type Chain, createWalletClient, custom } from "viem";
6-
import { getAddresses, requestAddresses, waitForTransactionReceipt } from "viem/actions";
75
import {
8-
applyChainId,
9-
ensureChainSelected,
10-
readPendingChainId,
11-
getChainById,
12-
} from "./utils/helpers.ts";
6+
type Address,
7+
type Chain,
8+
createWalletClient,
9+
custom,
10+
type TransactionReceipt,
11+
} from "viem";
12+
import { getAddresses, requestAddresses, waitForTransactionReceipt } from "viem/actions";
13+
import { applyChainId, api, isOk, renderJSON } from "./utils/helpers.ts";
1314
import type {
1415
ApiErr,
1516
ApiOk,
@@ -18,7 +19,6 @@ import type {
1819
EIP6963ProviderInfo,
1920
PendingAny,
2021
} from "./utils/types.ts";
21-
import { api, pick, readAddr, renderJSON } from "./utils/api.ts";
2222

2323
declare global {
2424
interface Window {
@@ -43,7 +43,7 @@ export function App() {
4343
const [account, setAccount] = useState<Address>();
4444
const [chainId, setChainId] = useState<number>();
4545
const [chain, setChain] = useState<Chain>();
46-
const [lastTxReceipt, setLastTxReceipt] = useState<any | null>(null);
46+
const [lastTxReceipt, setLastTxReceipt] = useState<TransactionReceipt | null>(null);
4747
const [lastTxHash, setLastTxHash] = useState<string | null>(null);
4848

4949
const pollRef = useRef<number | null>(null);
@@ -62,12 +62,12 @@ export function App() {
6262
const resp = await api<
6363
ApiOk<{ connected: boolean; account?: string; chainId?: number }> | ApiErr
6464
>("/api/connection");
65-
const ok = resp && (resp as ApiOk<any>).status === "ok";
66-
const data = ok ? (resp as ApiOk<any>).data : null;
6765

68-
const serverConnected = !!data?.connected;
69-
const serverAccount = (data?.account as string | undefined)?.toLowerCase();
70-
const serverChainId = data?.chainId as number | undefined;
66+
if (!isOk(resp)) return;
67+
68+
const serverConnected = !!resp.data?.connected;
69+
const serverAccount = (resp.data?.account as string | undefined)?.toLowerCase();
70+
const serverChainId = resp.data?.chainId as number | undefined;
7171

7272
if (!account || chainId == null) {
7373
if (serverConnected) {
@@ -134,67 +134,36 @@ export function App() {
134134
const tx = pending;
135135

136136
try {
137-
const targetChainId = readPendingChainId(tx) ?? chainId;
138-
await ensureChainSelected(selected.provider, targetChainId, chainId);
139-
const desiredChain =
140-
chain ?? (targetChainId != null ? getChainById(targetChainId) : undefined);
141-
if (!desiredChain) throw new Error("No chain metadata available");
142-
143-
try {
144-
const raw = await selected.provider.request<string>({ method: "eth_chainId" });
145-
applyChainId(raw, setChainId, setChain);
146-
} catch {}
137+
const hash = (await selected.provider.request({
138+
method: "eth_sendTransaction",
139+
params: [tx.request],
140+
})) as `0x${string}`;
141+
setLastTxHash(hash);
147142

148-
const { readHex } = await import("./utils/api.ts");
149-
const from =
150-
(account as `0x${string}` | undefined) ??
151-
(readAddr(tx, "from", "sender") as `0x${string}` | undefined);
152-
if (!from) throw new Error("No sender account available");
143+
const receipt = await waitForTransactionReceipt(walletClient, { hash });
144+
setLastTxReceipt(receipt);
153145

154-
const serverFrom = readAddr(tx, "from", "sender");
155-
if (serverFrom && account && serverFrom.toLowerCase() !== account.toLowerCase()) {
156-
throw new Error(`Server 'from' (${serverFrom}) != connected (${account})`);
157-
}
158-
159-
const to = readAddr(tx, "to");
160-
const value = pick(readHex(tx, "value", "amount"));
161-
const data = pick(readHex(tx, "data", "input", "calldata"));
162-
const gas = pick(readHex(tx, "gas", "gasLimit"));
163-
const gasPrice = pick(readHex(tx, "gasPrice"));
164-
const maxFeePerGas = pick(readHex(tx, "maxFeePerGas"));
165-
const maxPriorityFeePerGas = pick(readHex(tx, "maxPriorityFeePerGas"));
166-
const nonce = tx.nonce as number | undefined;
167-
168-
const params: any = { account: from, to, value, data, gas, nonce };
169-
if (gasPrice) params.gasPrice = gasPrice;
170-
else if (maxFeePerGas || maxPriorityFeePerGas) {
171-
params.maxFeePerGas = maxFeePerGas;
172-
params.maxPriorityFeePerGas = maxPriorityFeePerGas;
173-
}
174-
175-
const lastHash = await walletClient.sendTransaction({
176-
...params,
177-
chain: desiredChain,
178-
});
179-
console.log("tx sent:", { id: tx.id, hash: lastHash });
180-
setLastTxHash(lastHash);
181-
182-
const lastReceipt = await waitForTransactionReceipt(walletClient, { hash: lastHash });
183-
console.log("tx receipt:", lastReceipt);
184-
setLastTxReceipt(lastReceipt);
185-
186-
await api("/api/transaction/response", "POST", { id: tx.id, hash: lastHash, error: null });
146+
await api("/api/transaction/response", "POST", { id: tx.id, hash, error: null });
187147
await pollTick();
188-
} catch (e: any) {
189-
console.log("send failed:", String(e?.message ?? e));
148+
} catch (e: unknown) {
149+
const msg =
150+
typeof e === "object" &&
151+
e &&
152+
"message" in e &&
153+
typeof (e as { message?: unknown }).message === "string"
154+
? (e as { message: string }).message
155+
: String(e);
156+
157+
console.log("send failed:", msg);
190158

191159
try {
192160
await api("/api/transaction/response", "POST", {
193-
id: tx!.id,
161+
id: (pending as { id?: string }).id,
194162
hash: null,
195-
error: String(e?.message ?? e),
163+
error: msg,
196164
});
197165
} catch {}
166+
198167
await pollTick();
199168
}
200169
};

src/utils/api.ts

Lines changed: 0 additions & 46 deletions
This file was deleted.

src/utils/helpers.ts

Lines changed: 32 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
import type { Chain } from "viem";
1+
import { hexToBigInt, type Chain } from "viem";
22
import * as chains from "viem/chains";
3-
import type { EIP1193 } from "./types";
43

54
export const ALL_CHAINS: readonly Chain[] = Object.freeze(Object.values(chains) as Chain[]);
65
export const getChainById = (id: number) => ALL_CHAINS.find((c) => c.id === id);
@@ -20,58 +19,6 @@ const parseChainId = (input: unknown): number | undefined => {
2019
return undefined;
2120
};
2221

23-
export const readPendingChainId = (p: Record<string, unknown>): number | undefined =>
24-
parseChainId(p.chainId) ?? parseChainId((p as any).chain_id) ?? parseChainId((p as any).network);
25-
26-
export const toHexChainId = (id: number): `0x${string}` => `0x${id.toString(16)}` as `0x${string}`;
27-
28-
export const ensureChainSelected = async (
29-
provider: EIP1193,
30-
wantChainId?: number,
31-
haveChainId?: number,
32-
): Promise<void> => {
33-
if (!wantChainId || wantChainId === haveChainId) return;
34-
const chainIdHex = toHexChainId(wantChainId);
35-
try {
36-
await provider.request({
37-
method: "wallet_switchEthereumChain",
38-
params: [{ chainId: chainIdHex }],
39-
});
40-
return;
41-
} catch (err: any) {
42-
const code = err?.code ?? err?.data?.originalError?.code;
43-
if (code !== 4902) throw err;
44-
45-
const meta = getChainById(wantChainId);
46-
if (!meta) throw new Error(`Unknown chainId ${wantChainId}`);
47-
48-
const rpc = meta.rpcUrls?.default?.http?.length
49-
? meta.rpcUrls.default.http
50-
: (meta.rpcUrls?.public?.http ?? []);
51-
52-
await provider.request({
53-
method: "wallet_addEthereumChain",
54-
params: [
55-
{
56-
chainId: chainIdHex,
57-
chainName: meta.name,
58-
rpcUrls: rpc,
59-
nativeCurrency: (meta.nativeCurrency as {
60-
name: string;
61-
symbol: string;
62-
decimals: number;
63-
}) ?? { name: "Ether", symbol: "ETH", decimals: 18 },
64-
},
65-
],
66-
});
67-
68-
await provider.request({
69-
method: "wallet_switchEthereumChain",
70-
params: [{ chainId: chainIdHex }],
71-
});
72-
}
73-
};
74-
7522
export const applyChainId = (
7623
raw: unknown,
7724
setChainId: (n: number | undefined) => void,
@@ -81,3 +28,34 @@ export const applyChainId = (
8128
setChainId(id);
8229
setChain(id != null ? getChainById(id) : undefined);
8330
};
31+
32+
export const toBig = (h?: `0x${string}`) => (h ? hexToBigInt(h) : undefined);
33+
34+
import type { ApiErr, ApiOk } from "./types";
35+
36+
export const ENDPOINT = "http://127.0.0.1:9545";
37+
38+
export const api = async <T = unknown>(
39+
path: string,
40+
method: "GET" | "POST" = "GET",
41+
body?: unknown,
42+
): Promise<T> => {
43+
const res = await fetch(`${ENDPOINT}${path}`, {
44+
method,
45+
headers: { "Content-Type": "application/json" },
46+
body: body === undefined ? undefined : JSON.stringify(body),
47+
});
48+
if (!res.ok) throw new Error(`API request failed: ${res.status} ${res.statusText}`);
49+
try {
50+
return (await res.json()) as T;
51+
} catch {
52+
throw new Error("Invalid JSON response");
53+
}
54+
};
55+
56+
export const renderJSON = (obj: unknown) =>
57+
JSON.stringify(obj, (_k, v) => (typeof v === "bigint" ? v.toString() : v), 2);
58+
59+
export const isOk = <T>(r: ApiOk<T> | ApiErr | null | undefined): r is ApiOk<T> => {
60+
return !!r && (r as ApiOk<T>).status === "ok";
61+
};

0 commit comments

Comments
 (0)