@hfunlabs/hypurr-connect 0.1.11 → 0.1.13
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 +27 -2
- package/dist/index.d.ts +174 -10
- package/dist/index.js +2732 -23
- package/dist/index.js.map +1 -1
- package/package.json +5 -4
- package/src/DeleteWalletModal.tsx +344 -0
- package/src/HypurrConnectProvider.tsx +205 -41
- package/src/RenameWalletModal.tsx +325 -0
- package/src/UserProfileModal.tsx +982 -0
- package/src/WalletSelectorDropdown.tsx +797 -0
- package/src/agent.ts +2 -2
- package/src/icons/lucide.tsx +197 -0
- package/src/index.ts +16 -0
- package/src/privateKeySigner.ts +32 -0
- package/src/profileStyles.ts +213 -0
- package/src/types.ts +48 -10
- package/src/TelegramLoginWidget.tsx +0 -62
package/src/agent.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { StoredAgent } from "./types";
|
|
2
|
+
import { PrivateKeySigner } from "./privateKeySigner";
|
|
2
3
|
|
|
3
4
|
export const AGENT_NAME = "hypurr-connect";
|
|
4
5
|
|
|
@@ -27,7 +28,7 @@ export function clearAgent(masterAddress: string): void {
|
|
|
27
28
|
|
|
28
29
|
/**
|
|
29
30
|
* Generate a random 32-byte private key and derive its address using the
|
|
30
|
-
*
|
|
31
|
+
* local PrivateKeySigner compatibility wrapper.
|
|
31
32
|
*/
|
|
32
33
|
export async function generateAgentKey(): Promise<{
|
|
33
34
|
privateKey: `0x${string}`;
|
|
@@ -39,7 +40,6 @@ export async function generateAgentKey(): Promise<{
|
|
|
39
40
|
.join("");
|
|
40
41
|
const privateKey = `0x${hex}` as `0x${string}`;
|
|
41
42
|
|
|
42
|
-
const { PrivateKeySigner } = await import("@hfunlabs/hyperliquid/signing");
|
|
43
43
|
const signer = new PrivateKeySigner(privateKey);
|
|
44
44
|
return { privateKey, address: signer.address };
|
|
45
45
|
}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import type { CSSProperties, SVGProps } from "react";
|
|
2
|
+
|
|
3
|
+
interface IconProps extends Omit<SVGProps<SVGSVGElement>, "fill"> {
|
|
4
|
+
size?: number;
|
|
5
|
+
color?: string;
|
|
6
|
+
fill?: string;
|
|
7
|
+
style?: CSSProperties;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function svgBase({
|
|
11
|
+
size = 16,
|
|
12
|
+
color = "currentColor",
|
|
13
|
+
fill = "none",
|
|
14
|
+
...rest
|
|
15
|
+
}: IconProps) {
|
|
16
|
+
return {
|
|
17
|
+
width: size,
|
|
18
|
+
height: size,
|
|
19
|
+
viewBox: "0 0 24 24",
|
|
20
|
+
fill,
|
|
21
|
+
stroke: color,
|
|
22
|
+
strokeWidth: 2,
|
|
23
|
+
strokeLinecap: "round" as const,
|
|
24
|
+
strokeLinejoin: "round" as const,
|
|
25
|
+
...rest,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function Copy(props: IconProps) {
|
|
30
|
+
return (
|
|
31
|
+
<svg {...svgBase(props)}>
|
|
32
|
+
<rect width="14" height="14" x="8" y="8" rx="2" ry="2" />
|
|
33
|
+
<path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2" />
|
|
34
|
+
</svg>
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function Star(props: IconProps) {
|
|
39
|
+
return (
|
|
40
|
+
<svg {...svgBase(props)}>
|
|
41
|
+
<polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2" />
|
|
42
|
+
</svg>
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function User(props: IconProps) {
|
|
47
|
+
return (
|
|
48
|
+
<svg {...svgBase(props)}>
|
|
49
|
+
<path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2" />
|
|
50
|
+
<circle cx="12" cy="7" r="4" />
|
|
51
|
+
</svg>
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function Zap(props: IconProps) {
|
|
56
|
+
return (
|
|
57
|
+
<svg {...svgBase(props)}>
|
|
58
|
+
<polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2" />
|
|
59
|
+
</svg>
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function Wallet(props: IconProps) {
|
|
64
|
+
return (
|
|
65
|
+
<svg {...svgBase(props)}>
|
|
66
|
+
<path d="M20 12V8H6a2 2 0 0 1-2-2c0-1.1.9-2 2-2h12v4" />
|
|
67
|
+
<path d="M4 6v12c0 1.1.9 2 2 2h14v-4" />
|
|
68
|
+
<path d="M18 12a2 2 0 0 0-2 2c0 1.1.9 2 2 2h4v-4z" />
|
|
69
|
+
</svg>
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function TrendingUp(props: IconProps) {
|
|
74
|
+
return (
|
|
75
|
+
<svg {...svgBase(props)}>
|
|
76
|
+
<polyline points="22 7 13.5 15.5 8.5 10.5 2 17" />
|
|
77
|
+
<polyline points="16 7 22 7 22 13" />
|
|
78
|
+
</svg>
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function Trash2(props: IconProps) {
|
|
83
|
+
return (
|
|
84
|
+
<svg {...svgBase(props)}>
|
|
85
|
+
<path d="M3 6h18" />
|
|
86
|
+
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6" />
|
|
87
|
+
<path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" />
|
|
88
|
+
<line x1="10" x2="10" y1="11" y2="17" />
|
|
89
|
+
<line x1="14" x2="14" y1="11" y2="17" />
|
|
90
|
+
</svg>
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function Pencil(props: IconProps) {
|
|
95
|
+
return (
|
|
96
|
+
<svg {...svgBase(props)}>
|
|
97
|
+
<path d="M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z" />
|
|
98
|
+
<path d="m15 5 4 4" />
|
|
99
|
+
</svg>
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function LayoutDashboard(props: IconProps) {
|
|
104
|
+
return (
|
|
105
|
+
<svg {...svgBase(props)}>
|
|
106
|
+
<rect width="7" height="9" x="3" y="3" rx="1" />
|
|
107
|
+
<rect width="7" height="5" x="14" y="3" rx="1" />
|
|
108
|
+
<rect width="7" height="9" x="14" y="12" rx="1" />
|
|
109
|
+
<rect width="7" height="5" x="3" y="16" rx="1" />
|
|
110
|
+
</svg>
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export function AlertTriangle(props: IconProps) {
|
|
115
|
+
return (
|
|
116
|
+
<svg {...svgBase(props)}>
|
|
117
|
+
<path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3" />
|
|
118
|
+
<line x1="12" x2="12" y1="9" y2="13" />
|
|
119
|
+
<line x1="12" x2="12.01" y1="17" y2="17" />
|
|
120
|
+
</svg>
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export function Loader2(props: IconProps) {
|
|
125
|
+
const { size = 16, color = "currentColor", style, ...rest } = props;
|
|
126
|
+
return (
|
|
127
|
+
<svg
|
|
128
|
+
width={size}
|
|
129
|
+
height={size}
|
|
130
|
+
viewBox="0 0 24 24"
|
|
131
|
+
fill="none"
|
|
132
|
+
stroke={color}
|
|
133
|
+
strokeWidth={2}
|
|
134
|
+
strokeLinecap="round"
|
|
135
|
+
strokeLinejoin="round"
|
|
136
|
+
style={{
|
|
137
|
+
animation: "hypurr-spin 1s linear infinite",
|
|
138
|
+
...style,
|
|
139
|
+
}}
|
|
140
|
+
{...rest}
|
|
141
|
+
>
|
|
142
|
+
<path d="M21 12a9 9 0 1 1-6.219-8.56" />
|
|
143
|
+
</svg>
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export function Crown(props: IconProps) {
|
|
148
|
+
return (
|
|
149
|
+
<svg {...svgBase(props)}>
|
|
150
|
+
<path d="M11.562 3.266a.5.5 0 0 1 .876 0L15.39 8.87a1 1 0 0 0 1.516.294L21.183 5.5a.5.5 0 0 1 .798.519l-2.834 10.246a1 1 0 0 1-.956.734H5.81a1 1 0 0 1-.957-.734L2.02 6.02a.5.5 0 0 1 .798-.519l4.276 3.664a1 1 0 0 0 1.516-.294z" />
|
|
151
|
+
<path d="M5 21h14" />
|
|
152
|
+
</svg>
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export function Folder(props: IconProps) {
|
|
157
|
+
return (
|
|
158
|
+
<svg {...svgBase(props)}>
|
|
159
|
+
<path d="M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z" />
|
|
160
|
+
</svg>
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export function LogOut(props: IconProps) {
|
|
165
|
+
return (
|
|
166
|
+
<svg {...svgBase(props)}>
|
|
167
|
+
<path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4" />
|
|
168
|
+
<polyline points="16 17 21 12 16 7" />
|
|
169
|
+
<line x1="21" x2="9" y1="12" y2="12" />
|
|
170
|
+
</svg>
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export function Plus(props: IconProps) {
|
|
175
|
+
return (
|
|
176
|
+
<svg {...svgBase(props)}>
|
|
177
|
+
<path d="M5 12h14" />
|
|
178
|
+
<path d="M12 5v14" />
|
|
179
|
+
</svg>
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export function X(props: IconProps) {
|
|
184
|
+
return (
|
|
185
|
+
<svg {...svgBase(props)}>
|
|
186
|
+
<path d="M18 6 6 18" />
|
|
187
|
+
<path d="m6 6 12 12" />
|
|
188
|
+
</svg>
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/** Inject the keyframes used by {@link Loader2}. Mount once at the top of any modal that spins. */
|
|
193
|
+
export function SpinKeyframes() {
|
|
194
|
+
return (
|
|
195
|
+
<style>{`@keyframes hypurr-spin { to { transform: rotate(360deg); } }`}</style>
|
|
196
|
+
);
|
|
197
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -4,17 +4,33 @@ export {
|
|
|
4
4
|
} from "./HypurrConnectProvider";
|
|
5
5
|
export { LoginModal } from "./LoginModal";
|
|
6
6
|
export type { LoginModalProps } from "./LoginModal";
|
|
7
|
+
export { WalletSelectorDropdown } from "./WalletSelectorDropdown";
|
|
8
|
+
export type { WalletSelectorDropdownProps } from "./WalletSelectorDropdown";
|
|
9
|
+
export { UserProfileModal } from "./UserProfileModal";
|
|
10
|
+
export type { UserProfileModalProps, SlippageOption } from "./UserProfileModal";
|
|
11
|
+
export { DeleteWalletModal } from "./DeleteWalletModal";
|
|
12
|
+
export type { DeleteWalletModalProps } from "./DeleteWalletModal";
|
|
13
|
+
export { RenameWalletModal } from "./RenameWalletModal";
|
|
14
|
+
export type { RenameWalletModalProps } from "./RenameWalletModal";
|
|
15
|
+
export type { PrincipalColors, PrincipalColorOverrides } from "./profileStyles";
|
|
7
16
|
export { GrpcExchangeTransport } from "./GrpcExchangeTransport";
|
|
8
17
|
export type { GrpcExchangeTransportConfig } from "./GrpcExchangeTransport";
|
|
9
18
|
export { createTelegramClient, createStaticClient } from "./grpc";
|
|
19
|
+
export { PrivateKeySigner } from "./privateKeySigner";
|
|
10
20
|
export { createEoaSigner } from "./types";
|
|
11
21
|
export type {
|
|
12
22
|
AuthMethod,
|
|
23
|
+
EoaSignerOptions,
|
|
24
|
+
EoaSignTransactionFn,
|
|
13
25
|
EoaSigner,
|
|
26
|
+
EvmRequestFn,
|
|
27
|
+
EvmTransactionRequest,
|
|
28
|
+
Hex,
|
|
14
29
|
HypurrConnectConfig,
|
|
15
30
|
HypurrConnectState,
|
|
16
31
|
HypurrUser,
|
|
17
32
|
ScaleCreateParams,
|
|
33
|
+
SignEvmTransactionFn,
|
|
18
34
|
SignTypedDataFn,
|
|
19
35
|
StoredAgent,
|
|
20
36
|
TelegramLoginData,
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { AbstractViemLocalAccount } from "@hfunlabs/hyperliquid/signing";
|
|
2
|
+
import { privateKeyToAccount, type PrivateKeyAccount } from "viem/accounts";
|
|
3
|
+
import type { Hex } from "./types";
|
|
4
|
+
|
|
5
|
+
type SignTypedDataParams = Parameters<
|
|
6
|
+
AbstractViemLocalAccount["signTypedData"]
|
|
7
|
+
>[0];
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Compatibility wrapper for SDK versions that removed PrivateKeySigner.
|
|
11
|
+
*
|
|
12
|
+
* It exposes the viem local-account shape accepted by the Hyperliquid SDK.
|
|
13
|
+
*/
|
|
14
|
+
export class PrivateKeySigner implements AbstractViemLocalAccount {
|
|
15
|
+
#account: PrivateKeyAccount;
|
|
16
|
+
readonly address: Hex;
|
|
17
|
+
|
|
18
|
+
constructor(privateKey: string) {
|
|
19
|
+
this.#account = privateKeyToAccount(normalizePrivateKey(privateKey));
|
|
20
|
+
this.address = this.#account.address;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
signTypedData(params: SignTypedDataParams): Promise<Hex> {
|
|
24
|
+
return this.#account.signTypedData(
|
|
25
|
+
params as Parameters<PrivateKeyAccount["signTypedData"]>[0],
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function normalizePrivateKey(privateKey: string): Hex {
|
|
31
|
+
return (privateKey.startsWith("0x") ? privateKey : `0x${privateKey}`) as Hex;
|
|
32
|
+
}
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import type { CSSProperties } from "react";
|
|
2
|
+
|
|
3
|
+
export const profileColors = {
|
|
4
|
+
accent: "#a855f7",
|
|
5
|
+
panel: "rgba(14,18,24,0.8)",
|
|
6
|
+
panelSolid: "#0e1218",
|
|
7
|
+
backdrop: "rgba(0,0,0,0.7)",
|
|
8
|
+
border: "#1f2937",
|
|
9
|
+
surfaceBtn: "#0D1219",
|
|
10
|
+
surfaceBtnHover: "#15171A",
|
|
11
|
+
surfaceBtnActive: "#1E2124",
|
|
12
|
+
surfaceBd: "#262A30",
|
|
13
|
+
surfaceBdHover: "#444548",
|
|
14
|
+
surfaceBdActive: "#4B4D50",
|
|
15
|
+
text: "#ffffff",
|
|
16
|
+
muted: "#aab1c1",
|
|
17
|
+
subdued: "#7d8597",
|
|
18
|
+
disabled: "#4b5563",
|
|
19
|
+
danger: "#f87171",
|
|
20
|
+
dangerStrong: "#ef4444",
|
|
21
|
+
purple: "#d8b4fe",
|
|
22
|
+
yellow: "#eab308",
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const fontFamily = {
|
|
26
|
+
sans: "Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica Neue, Arial, sans-serif",
|
|
27
|
+
mono: "Google Sans Code, Roboto Mono, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace",
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export interface PrincipalColors {
|
|
31
|
+
accent: string;
|
|
32
|
+
accentText: string;
|
|
33
|
+
accentBackground: string;
|
|
34
|
+
accentBorder: string;
|
|
35
|
+
accentHoverBackground: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export type PrincipalColorOverrides = Partial<PrincipalColors>;
|
|
39
|
+
|
|
40
|
+
const colorWithAlpha = (color: string, alpha: number): string => {
|
|
41
|
+
const hex = color.trim();
|
|
42
|
+
const shortHex = /^#([0-9a-f]{3})$/i.exec(hex);
|
|
43
|
+
const longHex = /^#([0-9a-f]{6})$/i.exec(hex);
|
|
44
|
+
|
|
45
|
+
if (shortHex) {
|
|
46
|
+
const [r, g, b] = shortHex[1].split("").map((value) => value + value);
|
|
47
|
+
return `rgba(${parseInt(r, 16)},${parseInt(g, 16)},${parseInt(b, 16)},${alpha})`;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (longHex) {
|
|
51
|
+
const value = longHex[1];
|
|
52
|
+
return `rgba(${parseInt(value.slice(0, 2), 16)},${parseInt(
|
|
53
|
+
value.slice(2, 4),
|
|
54
|
+
16,
|
|
55
|
+
)},${parseInt(value.slice(4, 6), 16)},${alpha})`;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return `color-mix(in srgb, ${color} ${Math.round(alpha * 100)}%, transparent)`;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export const resolvePrincipalColors = (
|
|
62
|
+
accentColor?: string,
|
|
63
|
+
overrides: PrincipalColorOverrides = {},
|
|
64
|
+
): PrincipalColors => {
|
|
65
|
+
const hasCustomAccent = !!accentColor || !!overrides.accent;
|
|
66
|
+
const accent = overrides.accent ?? accentColor ?? profileColors.accent;
|
|
67
|
+
const accentText =
|
|
68
|
+
overrides.accentText ?? (hasCustomAccent ? accent : profileColors.purple);
|
|
69
|
+
|
|
70
|
+
return {
|
|
71
|
+
accent,
|
|
72
|
+
accentText,
|
|
73
|
+
accentBackground: overrides.accentBackground ?? colorWithAlpha(accent, 0.1),
|
|
74
|
+
accentBorder: overrides.accentBorder ?? colorWithAlpha(accent, 0.25),
|
|
75
|
+
accentHoverBackground:
|
|
76
|
+
overrides.accentHoverBackground ?? colorWithAlpha(accent, 0.1),
|
|
77
|
+
};
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
export const modalBackdropStyle = (zIndex: number): CSSProperties => ({
|
|
81
|
+
position: "fixed",
|
|
82
|
+
inset: 0,
|
|
83
|
+
zIndex,
|
|
84
|
+
background: profileColors.backdrop,
|
|
85
|
+
backdropFilter: "blur(4px)",
|
|
86
|
+
WebkitBackdropFilter: "blur(4px)",
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
export const modalWrapperStyle = (
|
|
90
|
+
zIndex: number,
|
|
91
|
+
padding = 0,
|
|
92
|
+
): CSSProperties => ({
|
|
93
|
+
position: "fixed",
|
|
94
|
+
inset: 0,
|
|
95
|
+
zIndex,
|
|
96
|
+
display: "flex",
|
|
97
|
+
alignItems: "center",
|
|
98
|
+
justifyContent: "center",
|
|
99
|
+
padding,
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
export const modalPanelStyle = (solid = false): CSSProperties => ({
|
|
103
|
+
position: "relative",
|
|
104
|
+
width: "100%",
|
|
105
|
+
maxWidth: 448,
|
|
106
|
+
background: solid ? profileColors.panelSolid : profileColors.panel,
|
|
107
|
+
backdropFilter: solid ? undefined : "blur(10px)",
|
|
108
|
+
WebkitBackdropFilter: solid ? undefined : "blur(10px)",
|
|
109
|
+
border: `1px solid ${profileColors.border}`,
|
|
110
|
+
borderRadius: 8,
|
|
111
|
+
boxShadow: "0 25px 50px -12px rgba(0,0,0,0.6)",
|
|
112
|
+
overflow: "hidden",
|
|
113
|
+
fontFamily: fontFamily.sans,
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
export const modalHeaderStyle: CSSProperties = {
|
|
117
|
+
position: "relative",
|
|
118
|
+
display: "flex",
|
|
119
|
+
alignItems: "center",
|
|
120
|
+
justifyContent: "center",
|
|
121
|
+
padding: "24px 24px 20px",
|
|
122
|
+
borderBottom: `1px solid ${profileColors.border}`,
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
export const titleStyle: CSSProperties = {
|
|
126
|
+
margin: 0,
|
|
127
|
+
fontSize: 14,
|
|
128
|
+
lineHeight: "1.25rem",
|
|
129
|
+
fontWeight: 600,
|
|
130
|
+
color: profileColors.text,
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
export const closeBtnStyle = (disabled = false): CSSProperties => ({
|
|
134
|
+
position: "absolute",
|
|
135
|
+
right: 24,
|
|
136
|
+
background: "transparent",
|
|
137
|
+
border: "none",
|
|
138
|
+
color: profileColors.muted,
|
|
139
|
+
cursor: disabled ? "not-allowed" : "pointer",
|
|
140
|
+
padding: 0,
|
|
141
|
+
display: "flex",
|
|
142
|
+
opacity: disabled ? 0.4 : 1,
|
|
143
|
+
transition: "color 150ms",
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
export const upperLabelStyle: CSSProperties = {
|
|
147
|
+
fontSize: 11,
|
|
148
|
+
lineHeight: "1rem",
|
|
149
|
+
fontWeight: 500,
|
|
150
|
+
textTransform: "uppercase",
|
|
151
|
+
letterSpacing: "0.1em",
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
export const raisedButtonStyle = (
|
|
155
|
+
state: "default" | "hover" | "active" | "disabled" = "default",
|
|
156
|
+
): CSSProperties => {
|
|
157
|
+
if (state === "active") {
|
|
158
|
+
return {
|
|
159
|
+
backgroundColor: profileColors.surfaceBtnActive,
|
|
160
|
+
backgroundImage:
|
|
161
|
+
"linear-gradient(to bottom, rgba(255,255,255,0.05), transparent)",
|
|
162
|
+
boxShadow:
|
|
163
|
+
"inset 0 1px 0 rgba(255,255,255,0.18), inset 0 -1px 0 rgba(0,0,0,0.45), inset 0 0 0 1px #4B4D50, 0 1px 2px rgba(0,0,0,0.5)",
|
|
164
|
+
color: profileColors.text,
|
|
165
|
+
border: "none",
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (state === "disabled") {
|
|
170
|
+
return {
|
|
171
|
+
backgroundColor: profileColors.surfaceBtn,
|
|
172
|
+
backgroundImage:
|
|
173
|
+
"linear-gradient(to bottom, rgba(255,255,255,0.02), transparent)",
|
|
174
|
+
boxShadow:
|
|
175
|
+
"inset 0 1px 0 rgba(255,255,255,0.05), inset 0 -1px 0 rgba(0,0,0,0.25), inset 0 0 0 1px #1c2026",
|
|
176
|
+
color: profileColors.subdued,
|
|
177
|
+
border: "none",
|
|
178
|
+
cursor: "not-allowed",
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (state === "hover") {
|
|
183
|
+
return {
|
|
184
|
+
backgroundColor: profileColors.surfaceBtnHover,
|
|
185
|
+
backgroundImage:
|
|
186
|
+
"linear-gradient(to bottom, rgba(255,255,255,0.04), transparent)",
|
|
187
|
+
boxShadow:
|
|
188
|
+
"inset 0 1px 0 rgba(255,255,255,0.14), inset 0 -1px 0 rgba(0,0,0,0.40), inset 0 0 0 1px #444548",
|
|
189
|
+
color: "#d1d5db",
|
|
190
|
+
border: "none",
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return {
|
|
195
|
+
backgroundColor: profileColors.surfaceBtn,
|
|
196
|
+
backgroundImage:
|
|
197
|
+
"linear-gradient(to bottom, rgba(255,255,255,0.03), transparent)",
|
|
198
|
+
boxShadow:
|
|
199
|
+
"inset 0 1px 0 rgba(255,255,255,0.10), inset 0 -1px 0 rgba(0,0,0,0.35), inset 0 0 0 1px #262A30",
|
|
200
|
+
color: profileColors.muted,
|
|
201
|
+
border: "none",
|
|
202
|
+
};
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
export const dangerOutlineButtonStyle = (
|
|
206
|
+
enabled: boolean,
|
|
207
|
+
hovered = false,
|
|
208
|
+
): CSSProperties => ({
|
|
209
|
+
background: enabled && hovered ? "rgba(248,113,113,0.1)" : "transparent",
|
|
210
|
+
border: `1px solid ${enabled ? profileColors.danger : "#374151"}`,
|
|
211
|
+
color: enabled ? profileColors.danger : profileColors.disabled,
|
|
212
|
+
cursor: enabled ? "pointer" : "not-allowed",
|
|
213
|
+
});
|
package/src/types.ts
CHANGED
|
@@ -25,19 +25,15 @@ export interface HypurrConnectConfig {
|
|
|
25
25
|
isTestnet?: boolean;
|
|
26
26
|
/** Polling interval in ms for TWAP/Scale session updates. Default 5000. Set 0 to disable. */
|
|
27
27
|
sessionPollInterval?: number;
|
|
28
|
-
telegram
|
|
29
|
-
/** Deprecated for the hub flow; retained for older consumers. */
|
|
30
|
-
botUsername?: string;
|
|
31
|
-
/** Deprecated for the hub flow; retained for older consumers. */
|
|
32
|
-
botId?: string;
|
|
33
|
-
/** Deprecated: Telegram login is now handled by the auth hub. */
|
|
34
|
-
useWidget?: boolean;
|
|
28
|
+
telegram?: {
|
|
35
29
|
/** Auth hub login URL. Defaults to https://auth.hypurr.fun/login. */
|
|
36
30
|
authHubUrl?: string;
|
|
37
31
|
/** Optional callback URL. Defaults to the current page without auth query params. */
|
|
38
32
|
returnTo?: string | (() => string);
|
|
39
33
|
/** Requested hub scopes. Defaults to the scopes required by this SDK. */
|
|
40
34
|
scope?: string | string[];
|
|
35
|
+
/** @deprecated Telegram login is handled by the auth hub; this option is ignored. */
|
|
36
|
+
useWidget?: boolean;
|
|
41
37
|
};
|
|
42
38
|
}
|
|
43
39
|
|
|
@@ -86,12 +82,52 @@ export type SignTypedDataFn = (params: {
|
|
|
86
82
|
message: Record<string, unknown>;
|
|
87
83
|
}) => Promise<`0x${string}`>;
|
|
88
84
|
|
|
85
|
+
export type Hex = `0x${string}`;
|
|
86
|
+
|
|
87
|
+
export interface EvmTransactionRequest {
|
|
88
|
+
from?: Hex;
|
|
89
|
+
to?: Hex;
|
|
90
|
+
gas?: Hex;
|
|
91
|
+
gasPrice?: Hex;
|
|
92
|
+
value?: Hex;
|
|
93
|
+
data?: Hex;
|
|
94
|
+
nonce?: Hex;
|
|
95
|
+
chainId?: Hex;
|
|
96
|
+
maxFeePerGas?: Hex;
|
|
97
|
+
maxPriorityFeePerGas?: Hex;
|
|
98
|
+
type?: Hex;
|
|
99
|
+
accessList?: unknown;
|
|
100
|
+
[key: string]: unknown;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export type EvmRequestFn = (args: {
|
|
104
|
+
method: string;
|
|
105
|
+
params?: unknown[];
|
|
106
|
+
}) => Promise<unknown>;
|
|
107
|
+
|
|
108
|
+
export type SignEvmTransactionFn = (
|
|
109
|
+
transaction: EvmTransactionRequest,
|
|
110
|
+
) => Promise<Hex>;
|
|
111
|
+
|
|
112
|
+
export type EoaSignTransactionFn = (
|
|
113
|
+
transaction: EvmTransactionRequest,
|
|
114
|
+
) => Promise<unknown>;
|
|
115
|
+
|
|
89
116
|
/** Wallet signer provided at EOA connect time for user-signed actions. */
|
|
90
117
|
export interface EoaSigner {
|
|
91
118
|
signTypedData: SignTypedDataFn;
|
|
119
|
+
/** Optional raw EVM transaction signer. Used by `signEvmTransaction()` in EOA mode. */
|
|
120
|
+
signTransaction?: EoaSignTransactionFn;
|
|
121
|
+
/** Optional EIP-1193 provider request function. Used as a fallback for `eth_signTransaction`. */
|
|
122
|
+
request?: EvmRequestFn;
|
|
92
123
|
chainId: number;
|
|
93
124
|
}
|
|
94
125
|
|
|
126
|
+
export interface EoaSignerOptions {
|
|
127
|
+
signTransaction?: EoaSignTransactionFn;
|
|
128
|
+
request?: EvmRequestFn;
|
|
129
|
+
}
|
|
130
|
+
|
|
95
131
|
/**
|
|
96
132
|
* Create an {@link EoaSigner} from any EIP-712 signing function.
|
|
97
133
|
*
|
|
@@ -120,6 +156,7 @@ export function createEoaSigner(
|
|
|
120
156
|
| ((args: Record<string, unknown>) => Promise<`0x${string}`>)
|
|
121
157
|
| { current: (args: Record<string, unknown>) => Promise<`0x${string}`> },
|
|
122
158
|
chainId: number,
|
|
159
|
+
options: EoaSignerOptions = {},
|
|
123
160
|
): EoaSigner {
|
|
124
161
|
const resolve =
|
|
125
162
|
typeof signTypedDataAsync === "function"
|
|
@@ -127,6 +164,8 @@ export function createEoaSigner(
|
|
|
127
164
|
: (args: Record<string, unknown>) => signTypedDataAsync.current(args);
|
|
128
165
|
return {
|
|
129
166
|
signTypedData: (params) => resolve(params),
|
|
167
|
+
signTransaction: options.signTransaction,
|
|
168
|
+
request: options.request,
|
|
130
169
|
chainId,
|
|
131
170
|
};
|
|
132
171
|
}
|
|
@@ -174,6 +213,7 @@ export interface HypurrConnectState {
|
|
|
174
213
|
// Wallet management (Telegram only)
|
|
175
214
|
createWallet: (name: string) => Promise<HyperliquidWallet>;
|
|
176
215
|
deleteWallet: (walletId: number) => Promise<void>;
|
|
216
|
+
renameWallet: (walletId: number, name: string) => Promise<void>;
|
|
177
217
|
refreshWallets: () => void;
|
|
178
218
|
|
|
179
219
|
// Wallet packs & labels (Telegram only)
|
|
@@ -216,6 +256,7 @@ export interface HypurrConnectState {
|
|
|
216
256
|
|
|
217
257
|
// Auth actions
|
|
218
258
|
connectEoa: (address: `0x${string}`, signer?: EoaSigner) => void;
|
|
259
|
+
signEvmTransaction: SignEvmTransactionFn;
|
|
219
260
|
approveAgent: (
|
|
220
261
|
signTypedDataAsync: SignTypedDataFn,
|
|
221
262
|
chainId: number,
|
|
@@ -227,9 +268,6 @@ export interface HypurrConnectState {
|
|
|
227
268
|
agentReady: boolean;
|
|
228
269
|
clearAgent: () => void;
|
|
229
270
|
|
|
230
|
-
// Telegram config
|
|
231
|
-
botId: string;
|
|
232
|
-
|
|
233
271
|
// Low-level access
|
|
234
272
|
/** Deprecated: JWT auth leaves authData empty; use `telegramRpcOptions` for low-level calls. */
|
|
235
273
|
authDataMap: Record<string, string>;
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import { useEffect, useRef } from "react";
|
|
2
|
-
import type { TelegramLoginData } from "./types";
|
|
3
|
-
|
|
4
|
-
const WIDGET_SCRIPT_URL = "https://telegram.org/js/telegram-widget.js?22";
|
|
5
|
-
const CALLBACK_NAME = "__hypurrConnectTelegramAuth";
|
|
6
|
-
|
|
7
|
-
export interface TelegramLoginWidgetProps {
|
|
8
|
-
botUsername: string;
|
|
9
|
-
onAuth: (data: TelegramLoginData) => void;
|
|
10
|
-
buttonSize?: "large" | "medium" | "small";
|
|
11
|
-
cornerRadius?: number;
|
|
12
|
-
showUserPhoto?: boolean;
|
|
13
|
-
requestAccess?: boolean;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export function TelegramLoginWidget({
|
|
17
|
-
botUsername,
|
|
18
|
-
onAuth,
|
|
19
|
-
buttonSize = "large",
|
|
20
|
-
cornerRadius,
|
|
21
|
-
showUserPhoto = true,
|
|
22
|
-
requestAccess = true,
|
|
23
|
-
}: TelegramLoginWidgetProps) {
|
|
24
|
-
const containerRef = useRef<HTMLDivElement>(null);
|
|
25
|
-
const onAuthRef = useRef(onAuth);
|
|
26
|
-
onAuthRef.current = onAuth;
|
|
27
|
-
|
|
28
|
-
useEffect(() => {
|
|
29
|
-
const container = containerRef.current;
|
|
30
|
-
if (!container) return;
|
|
31
|
-
|
|
32
|
-
(window as unknown as Record<string, unknown>)[CALLBACK_NAME] = (
|
|
33
|
-
user: TelegramLoginData,
|
|
34
|
-
) => {
|
|
35
|
-
onAuthRef.current(user);
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
const script = document.createElement("script");
|
|
39
|
-
script.src = WIDGET_SCRIPT_URL;
|
|
40
|
-
script.async = true;
|
|
41
|
-
script.setAttribute("data-telegram-login", botUsername);
|
|
42
|
-
script.setAttribute("data-size", buttonSize);
|
|
43
|
-
script.setAttribute("data-onauth", `${CALLBACK_NAME}(user)`);
|
|
44
|
-
script.setAttribute("data-userpic", String(showUserPhoto));
|
|
45
|
-
if (requestAccess) {
|
|
46
|
-
script.setAttribute("data-request-access", "write");
|
|
47
|
-
}
|
|
48
|
-
if (cornerRadius !== undefined) {
|
|
49
|
-
script.setAttribute("data-radius", String(cornerRadius));
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
container.innerHTML = "";
|
|
53
|
-
container.appendChild(script);
|
|
54
|
-
|
|
55
|
-
return () => {
|
|
56
|
-
container.innerHTML = "";
|
|
57
|
-
delete (window as unknown as Record<string, unknown>)[CALLBACK_NAME];
|
|
58
|
-
};
|
|
59
|
-
}, [botUsername, buttonSize, cornerRadius, showUserPhoto, requestAccess]);
|
|
60
|
-
|
|
61
|
-
return <div ref={containerRef} />;
|
|
62
|
-
}
|