@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/README.md +162 -40
- package/dist/index.d.ts +32 -6
- package/dist/index.js +306 -141
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/HypurrConnectProvider.tsx +328 -158
- package/src/LoginModal.tsx +2 -2
- package/src/agent.ts +80 -0
- package/src/index.ts +4 -0
- package/src/types.ts +38 -7
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
|
-
//
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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
|
-
|
|
84
|
-
|
|
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
|