@hfunlabs/hypurr-connect 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/agent.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  import type { StoredAgent } from "./types";
2
2
 
3
+ export const AGENT_NAME = "hypurr-connect";
4
+
3
5
  const AGENT_STORAGE_PREFIX = "hypurr-connect-agent";
4
6
 
5
7
  function storageKey(masterAddress: string): string {
@@ -41,3 +43,81 @@ export async function generateAgentKey(): Promise<{
41
43
  const signer = new PrivateKeySigner(privateKey);
42
44
  return { privateKey, address: signer.address };
43
45
  }
46
+
47
+ interface ExtraAgent {
48
+ address: string;
49
+ name: string;
50
+ validUntil: number;
51
+ }
52
+
53
+ /**
54
+ * Query the Hyperliquid info API for the named agents registered to a user.
55
+ * Returns the matching entry for AGENT_NAME if it exists and is still valid.
56
+ */
57
+ export async function fetchActiveAgent(
58
+ userAddress: string,
59
+ isTestnet: boolean,
60
+ ): Promise<ExtraAgent | null> {
61
+ const url = isTestnet
62
+ ? "https://api.hyperliquid-testnet.xyz/info"
63
+ : "https://api.hyperliquid.xyz/info";
64
+
65
+ const res = await fetch(url, {
66
+ method: "POST",
67
+ headers: { "Content-Type": "application/json" },
68
+ body: JSON.stringify({ type: "extraAgents", user: userAddress }),
69
+ });
70
+
71
+ if (!res.ok) return null;
72
+
73
+ const agents: unknown = await res.json();
74
+ if (!Array.isArray(agents)) return null;
75
+
76
+ const nowMs = Date.now();
77
+ const match = (agents as ExtraAgent[]).find(
78
+ (a) => a.name === AGENT_NAME && a.validUntil * 1000 > nowMs,
79
+ );
80
+ if (!match) return null;
81
+ return { ...match, validUntil: match.validUntil * 1000 };
82
+ }
83
+
84
+ /**
85
+ * Checks whether a stored agent is still valid: the address must appear in the
86
+ * on-chain `extraAgents` list and not be expired.
87
+ */
88
+ export async function isAgentValid(
89
+ stored: StoredAgent,
90
+ userAddress: string,
91
+ isTestnet: boolean,
92
+ ): Promise<boolean> {
93
+ if (stored.validUntil <= Date.now()) return false;
94
+
95
+ const remote = await fetchActiveAgent(userAddress, isTestnet);
96
+ if (!remote) return false;
97
+
98
+ return (
99
+ remote.address.toLowerCase() === stored.address.toLowerCase() &&
100
+ remote.validUntil > Date.now()
101
+ );
102
+ }
103
+
104
+ const DEAD_AGENT_PATTERNS = [
105
+ /agent address .+ is not valid/i,
106
+ /unknown signer/i,
107
+ /not authorized/i,
108
+ /not an agent/i,
109
+ ];
110
+
111
+ /**
112
+ * Returns true if the error indicates the agent has been pruned, expired,
113
+ * or is otherwise no longer registered on-chain.
114
+ */
115
+ export function isDeadAgentError(err: unknown): boolean {
116
+ const msg =
117
+ err instanceof Error
118
+ ? err.message
119
+ : typeof err === "object" && err !== null && "message" in err
120
+ ? String((err as { message: unknown }).message)
121
+ : String(err);
122
+ return DEAD_AGENT_PATTERNS.some((p) => p.test(msg));
123
+ }
package/src/index.ts CHANGED
@@ -16,3 +16,7 @@ export type {
16
16
  StoredAgent,
17
17
  TelegramLoginData,
18
18
  } from "./types";
19
+
20
+ // Re-export wallet types from hypurr-grpc so consumers don't need the dependency
21
+ export type { HyperliquidWallet } from "hypurr-grpc/ts/hypurr/wallet";
22
+ export type { TelegramChatWalletPack } from "hypurr-grpc/ts/hypurr/user";
package/src/types.ts CHANGED
@@ -1,6 +1,8 @@
1
1
  import type { ExchangeClient } from "@hfunlabs/hyperliquid";
2
2
  import type { StaticClient } from "hypurr-grpc/ts/hypurr/static/static_service.client";
3
3
  import type { TelegramClient } from "hypurr-grpc/ts/hypurr/telegram/telegram_service.client";
4
+ import type { HyperliquidWallet } from "hypurr-grpc/ts/hypurr/wallet";
5
+ import type { TelegramChatWalletPack } from "hypurr-grpc/ts/hypurr/user";
4
6
 
5
7
  // ─── Config ──────────────────────────────────────────────────────
6
8
 
@@ -36,6 +38,8 @@ export interface HypurrUser {
36
38
  photoUrl?: string;
37
39
  authMethod: AuthMethod;
38
40
  telegramId?: string;
41
+ hfunScore?: number;
42
+ reputationScore?: number;
39
43
  }
40
44
 
41
45
  // ─── Agent (EOA flow) ────────────────────────────────────────────
@@ -44,6 +48,8 @@ export interface StoredAgent {
44
48
  privateKey: `0x${string}`;
45
49
  address: `0x${string}`;
46
50
  approvedAt: number;
51
+ /** Epoch ms from the `extraAgents` response; agent is invalid after this time. */
52
+ validUntil: number;
47
53
  }
48
54
 
49
55
  export type SignTypedDataFn = (params: {
@@ -69,10 +75,33 @@ export interface HypurrConnectState {
69
75
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
70
76
  exchange: ExchangeClient<any> | null;
71
77
 
72
- // USDC balance from Hyperliquid perps clearinghouse
73
- usdcBalance: string | null;
74
- usdcBalanceLoading: boolean;
75
- refreshBalance: () => void;
78
+ // Multi-wallet (Telegram only EOA has a single wallet)
79
+ wallets: HyperliquidWallet[];
80
+ selectedWalletId: number;
81
+ selectWallet: (walletId: number) => void;
82
+
83
+ // Wallet management (Telegram only)
84
+ createWallet: (name: string) => Promise<HyperliquidWallet>;
85
+ deleteWallet: (walletId: number) => Promise<void>;
86
+ refreshWallets: () => void;
87
+
88
+ // Wallet packs & labels (Telegram only)
89
+ packs: TelegramChatWalletPack[];
90
+ createWalletPack: (name: string) => Promise<number>;
91
+ addPackLabel: (params: {
92
+ walletAddress: string;
93
+ walletLabel: string;
94
+ packId: number;
95
+ }) => Promise<void>;
96
+ modifyPackLabel: (params: {
97
+ walletLabelOld: string;
98
+ walletLabelNew: string;
99
+ packId: number;
100
+ }) => Promise<void>;
101
+ removePackLabel: (params: {
102
+ walletLabel: string;
103
+ packId: number;
104
+ }) => Promise<void>;
76
105
 
77
106
  // Login modal
78
107
  loginModalOpen: boolean;
@@ -80,14 +109,16 @@ export interface HypurrConnectState {
80
109
  closeLoginModal: () => void;
81
110
 
82
111
  // Auth actions
83
- loginTelegram: (data: TelegramLoginData) => void;
84
- loginEoa: (address: `0x${string}`) => void;
112
+ connectEoa: (address: `0x${string}`) => void;
113
+ approveAgent: (
114
+ signTypedDataAsync: SignTypedDataFn,
115
+ chainId: number,
116
+ ) => Promise<void>;
85
117
  logout: () => void;
86
118
 
87
119
  // EOA agent management
88
120
  agent: StoredAgent | null;
89
121
  agentReady: boolean;
90
- approveAgent: (signTypedDataAsync: SignTypedDataFn) => Promise<void>;
91
122
  clearAgent: () => void;
92
123
 
93
124
  // Telegram config