@nexstone/rift-cli 0.1.1
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/LICENSE +201 -0
- package/bin/run.js +22 -0
- package/dist/commands/algo.d.ts +32 -0
- package/dist/commands/algo.js +719 -0
- package/dist/commands/audit.d.ts +13 -0
- package/dist/commands/audit.js +37 -0
- package/dist/commands/auth-status.d.ts +14 -0
- package/dist/commands/auth-status.js +118 -0
- package/dist/commands/auth.d.ts +14 -0
- package/dist/commands/auth.js +275 -0
- package/dist/commands/backtest.d.ts +26 -0
- package/dist/commands/backtest.js +283 -0
- package/dist/commands/collect/start.d.ts +11 -0
- package/dist/commands/collect/start.js +78 -0
- package/dist/commands/collect/status.d.ts +6 -0
- package/dist/commands/collect/status.js +60 -0
- package/dist/commands/compare.d.ts +16 -0
- package/dist/commands/compare.js +130 -0
- package/dist/commands/config.d.ts +16 -0
- package/dist/commands/config.js +143 -0
- package/dist/commands/cost.d.ts +20 -0
- package/dist/commands/cost.js +104 -0
- package/dist/commands/cross-asset.d.ts +14 -0
- package/dist/commands/cross-asset.js +39 -0
- package/dist/commands/data/fetch.d.ts +15 -0
- package/dist/commands/data/fetch.js +82 -0
- package/dist/commands/data/list.d.ts +6 -0
- package/dist/commands/data/list.js +28 -0
- package/dist/commands/data-inventory.d.ts +9 -0
- package/dist/commands/data-inventory.js +24 -0
- package/dist/commands/deposit.d.ts +10 -0
- package/dist/commands/deposit.js +222 -0
- package/dist/commands/doctor.d.ts +6 -0
- package/dist/commands/doctor.js +87 -0
- package/dist/commands/funding-browser.d.ts +12 -0
- package/dist/commands/funding-browser.js +33 -0
- package/dist/commands/guide.d.ts +6 -0
- package/dist/commands/guide.js +15 -0
- package/dist/commands/home.d.ts +23 -0
- package/dist/commands/home.js +210 -0
- package/dist/commands/init.d.ts +7 -0
- package/dist/commands/init.js +122 -0
- package/dist/commands/install.d.ts +9 -0
- package/dist/commands/install.js +89 -0
- package/dist/commands/interactive.d.ts +17 -0
- package/dist/commands/interactive.js +179 -0
- package/dist/commands/lessons.d.ts +12 -0
- package/dist/commands/lessons.js +33 -0
- package/dist/commands/montecarlo.d.ts +19 -0
- package/dist/commands/montecarlo.js +168 -0
- package/dist/commands/more.d.ts +11 -0
- package/dist/commands/more.js +227 -0
- package/dist/commands/new.d.ts +14 -0
- package/dist/commands/new.js +306 -0
- package/dist/commands/pairs.d.ts +22 -0
- package/dist/commands/pairs.js +147 -0
- package/dist/commands/perp/close.d.ts +12 -0
- package/dist/commands/perp/close.js +57 -0
- package/dist/commands/perp/long.d.ts +14 -0
- package/dist/commands/perp/long.js +38 -0
- package/dist/commands/perp/short.d.ts +14 -0
- package/dist/commands/perp/short.js +27 -0
- package/dist/commands/perp/status.d.ts +9 -0
- package/dist/commands/perp/status.js +26 -0
- package/dist/commands/portfolio/alerts.d.ts +6 -0
- package/dist/commands/portfolio/alerts.js +47 -0
- package/dist/commands/portfolio/backtest.d.ts +12 -0
- package/dist/commands/portfolio/backtest.js +178 -0
- package/dist/commands/portfolio/create.d.ts +7 -0
- package/dist/commands/portfolio/create.js +195 -0
- package/dist/commands/portfolio/start.d.ts +9 -0
- package/dist/commands/portfolio/start.js +64 -0
- package/dist/commands/portfolio/status.d.ts +6 -0
- package/dist/commands/portfolio/status.js +128 -0
- package/dist/commands/portfolio/stop.d.ts +6 -0
- package/dist/commands/portfolio/stop.js +81 -0
- package/dist/commands/portfolio-backtest.d.ts +13 -0
- package/dist/commands/portfolio-backtest.js +37 -0
- package/dist/commands/portfolio-matrix.d.ts +12 -0
- package/dist/commands/portfolio-matrix.js +30 -0
- package/dist/commands/quick-test.d.ts +17 -0
- package/dist/commands/quick-test.js +45 -0
- package/dist/commands/research.d.ts +57 -0
- package/dist/commands/research.js +1976 -0
- package/dist/commands/scout.d.ts +14 -0
- package/dist/commands/scout.js +184 -0
- package/dist/commands/serve.d.ts +9 -0
- package/dist/commands/serve.js +1176 -0
- package/dist/commands/setup/proxy.d.ts +10 -0
- package/dist/commands/setup/proxy.js +267 -0
- package/dist/commands/spot/buy.d.ts +14 -0
- package/dist/commands/spot/buy.js +38 -0
- package/dist/commands/spot/sell.d.ts +14 -0
- package/dist/commands/spot/sell.js +39 -0
- package/dist/commands/strategies/list.d.ts +6 -0
- package/dist/commands/strategies/list.js +34 -0
- package/dist/commands/sweep.d.ts +19 -0
- package/dist/commands/sweep.js +137 -0
- package/dist/commands/sync.d.ts +17 -0
- package/dist/commands/sync.js +54 -0
- package/dist/commands/test-trade.d.ts +6 -0
- package/dist/commands/test-trade.js +97 -0
- package/dist/commands/trade.d.ts +26 -0
- package/dist/commands/trade.js +274 -0
- package/dist/commands/transfer.d.ts +13 -0
- package/dist/commands/transfer.js +65 -0
- package/dist/commands/verify.d.ts +16 -0
- package/dist/commands/verify.js +38 -0
- package/dist/commands/walkforward.d.ts +20 -0
- package/dist/commands/walkforward.js +191 -0
- package/dist/commands/withdraw.d.ts +12 -0
- package/dist/commands/withdraw.js +55 -0
- package/dist/commands/workbench-create.d.ts +13 -0
- package/dist/commands/workbench-create.js +39 -0
- package/dist/lib/account-mode.d.ts +44 -0
- package/dist/lib/account-mode.js +96 -0
- package/dist/lib/analyzer.d.ts +4 -0
- package/dist/lib/analyzer.js +62 -0
- package/dist/lib/base-command.d.ts +35 -0
- package/dist/lib/base-command.js +49 -0
- package/dist/lib/credentials.d.ts +46 -0
- package/dist/lib/credentials.js +137 -0
- package/dist/lib/engine-passthrough.d.ts +28 -0
- package/dist/lib/engine-passthrough.js +60 -0
- package/dist/lib/fees.d.ts +52 -0
- package/dist/lib/fees.js +97 -0
- package/dist/lib/python-bridge.d.ts +24 -0
- package/dist/lib/python-bridge.js +182 -0
- package/dist/lib/setup-status.d.ts +32 -0
- package/dist/lib/setup-status.js +121 -0
- package/dist/lib/status-footer.d.ts +35 -0
- package/dist/lib/status-footer.js +101 -0
- package/dist/lib/tui.d.ts +130 -0
- package/dist/lib/tui.js +300 -0
- package/dist/lib/walletconnect.d.ts +70 -0
- package/dist/lib/walletconnect.js +407 -0
- package/package.json +49 -0
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WalletConnect integration for wallet approvals and fund management.
|
|
3
|
+
*
|
|
4
|
+
* Used for:
|
|
5
|
+
* 1. Auth setup — ApproveAgent + ApproveBuilderFee (one-time)
|
|
6
|
+
* 2. Withdrawals — withdraw_from_bridge (occasional, needs main wallet)
|
|
7
|
+
* 3. Transfers — usdClassTransfer spot↔perps (occasional, needs main wallet)
|
|
8
|
+
* 4. Deposits — Arbitrum bridge interaction (occasional, needs main wallet)
|
|
9
|
+
*
|
|
10
|
+
* Sessions persist across CLI invocations via SQLite storage at ~/.rift/walletconnect.db.
|
|
11
|
+
* Trading uses the agent key locally — no WalletConnect needed per trade.
|
|
12
|
+
*/
|
|
13
|
+
import SignClient from '@walletconnect/sign-client';
|
|
14
|
+
export interface ApprovalResult {
|
|
15
|
+
success: boolean;
|
|
16
|
+
signature?: string;
|
|
17
|
+
nonce?: number;
|
|
18
|
+
action?: Record<string, any>;
|
|
19
|
+
error?: string;
|
|
20
|
+
}
|
|
21
|
+
export interface WalletSession {
|
|
22
|
+
client: SignClient;
|
|
23
|
+
topic: string;
|
|
24
|
+
account: string;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Resume an existing WalletConnect session from disk.
|
|
28
|
+
* Returns null if no valid session exists (user needs to scan QR).
|
|
29
|
+
*/
|
|
30
|
+
export declare function getExistingSession(): Promise<WalletSession | null>;
|
|
31
|
+
/**
|
|
32
|
+
* Connect a new wallet via QR code scan.
|
|
33
|
+
* Session persists automatically via SQLite storage.
|
|
34
|
+
*/
|
|
35
|
+
export declare function connectWallet(onQRCode: (uri: string) => void, onStatus: (msg: string) => void): Promise<WalletSession>;
|
|
36
|
+
/**
|
|
37
|
+
* Request an EIP-712 signature via WalletConnect (persisted session).
|
|
38
|
+
* Sends push notification to user's phone. Returns null if no session.
|
|
39
|
+
*/
|
|
40
|
+
export declare function requestSignature(typedData: Record<string, any>): Promise<string | null>;
|
|
41
|
+
/**
|
|
42
|
+
* Request ApproveAgent signature via WalletConnect.
|
|
43
|
+
*/
|
|
44
|
+
export declare function requestAgentApproval(session: WalletSession, agentAddress: string, isMainnet?: boolean): Promise<ApprovalResult>;
|
|
45
|
+
/**
|
|
46
|
+
* Request ApproveBuilderFee signature via WalletConnect.
|
|
47
|
+
*/
|
|
48
|
+
export declare function requestBuilderFeeApproval(session: WalletSession, isMainnet?: boolean): Promise<ApprovalResult>;
|
|
49
|
+
/**
|
|
50
|
+
* Request withdrawal signature via WalletConnect.
|
|
51
|
+
* User approves on phone, USDC bridges from HL to Arbitrum.
|
|
52
|
+
*/
|
|
53
|
+
export declare function requestWithdrawal(amount: string, destination: string, isMainnet?: boolean): Promise<ApprovalResult>;
|
|
54
|
+
/**
|
|
55
|
+
* Request spot↔perps transfer signature via WalletConnect.
|
|
56
|
+
*/
|
|
57
|
+
export declare function requestTransfer(amount: string, toPerp: boolean, isMainnet?: boolean): Promise<ApprovalResult>;
|
|
58
|
+
/**
|
|
59
|
+
* Post a signed action to Hyperliquid's exchange API.
|
|
60
|
+
*/
|
|
61
|
+
export declare function postToHyperliquid(action: Record<string, any>, signature: string, nonce: number, isMainnet?: boolean): Promise<Record<string, any>>;
|
|
62
|
+
export declare const postApprovalToHyperliquid: typeof postToHyperliquid;
|
|
63
|
+
/**
|
|
64
|
+
* Display QR code in terminal.
|
|
65
|
+
*/
|
|
66
|
+
export declare function showQRCode(uri: string): void;
|
|
67
|
+
/**
|
|
68
|
+
* Disconnect WalletConnect session (only if user explicitly requests).
|
|
69
|
+
*/
|
|
70
|
+
export declare function disconnectWallet(session: WalletSession): Promise<void>;
|
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WalletConnect integration for wallet approvals and fund management.
|
|
3
|
+
*
|
|
4
|
+
* Used for:
|
|
5
|
+
* 1. Auth setup — ApproveAgent + ApproveBuilderFee (one-time)
|
|
6
|
+
* 2. Withdrawals — withdraw_from_bridge (occasional, needs main wallet)
|
|
7
|
+
* 3. Transfers — usdClassTransfer spot↔perps (occasional, needs main wallet)
|
|
8
|
+
* 4. Deposits — Arbitrum bridge interaction (occasional, needs main wallet)
|
|
9
|
+
*
|
|
10
|
+
* Sessions persist across CLI invocations via SQLite storage at ~/.rift/walletconnect.db.
|
|
11
|
+
* Trading uses the agent key locally — no WalletConnect needed per trade.
|
|
12
|
+
*/
|
|
13
|
+
import * as os from 'node:os';
|
|
14
|
+
import * as path from 'node:path';
|
|
15
|
+
import SignClient from '@walletconnect/sign-client';
|
|
16
|
+
// @ts-ignore — no types available for qrcode-terminal
|
|
17
|
+
import qrcode from 'qrcode-terminal';
|
|
18
|
+
import { BUILDER_ADDRESS, BUILDER_FEE_RATE } from './fees.js';
|
|
19
|
+
// Register at cloud.walletconnect.com — free tier
|
|
20
|
+
const WALLETCONNECT_PROJECT_ID = '315aa4af2eda0390c8411a5b5e9b4f7a';
|
|
21
|
+
// Persistent session storage
|
|
22
|
+
const WC_DB_PATH = path.join(os.homedir(), '.rift', 'walletconnect.db');
|
|
23
|
+
// Hyperliquid EIP-712 constants
|
|
24
|
+
const HL_SIGNATURE_CHAIN_ID = '0x66eee'; // 421614 decimal
|
|
25
|
+
const HL_SIGNATURE_CHAIN_ID_DECIMAL = parseInt(HL_SIGNATURE_CHAIN_ID, 16);
|
|
26
|
+
// Hyperliquid API URL (mainnet-only post-testnet rip)
|
|
27
|
+
const HL_MAINNET_API = 'https://api.hyperliquid.xyz';
|
|
28
|
+
// ─── SESSION MANAGEMENT ────────────────────────────────────
|
|
29
|
+
/**
|
|
30
|
+
* Get or create the SignClient with persistent SQLite storage.
|
|
31
|
+
* Sessions survive process restarts — no re-scan needed.
|
|
32
|
+
*/
|
|
33
|
+
async function getSignClient() {
|
|
34
|
+
return SignClient.init({
|
|
35
|
+
projectId: WALLETCONNECT_PROJECT_ID,
|
|
36
|
+
metadata: {
|
|
37
|
+
name: 'RIFT',
|
|
38
|
+
description: 'Algorithmic trading engine for Hyperliquid',
|
|
39
|
+
url: 'https://nexstone.io',
|
|
40
|
+
icons: ['https://nexstone.io/nexstone-logo.png'],
|
|
41
|
+
},
|
|
42
|
+
storageOptions: {
|
|
43
|
+
database: WC_DB_PATH,
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Resume an existing WalletConnect session from disk.
|
|
49
|
+
* Returns null if no valid session exists (user needs to scan QR).
|
|
50
|
+
*/
|
|
51
|
+
export async function getExistingSession() {
|
|
52
|
+
try {
|
|
53
|
+
const client = await getSignClient();
|
|
54
|
+
const sessions = client.session.getAll();
|
|
55
|
+
const now = Math.floor(Date.now() / 1000);
|
|
56
|
+
const valid = sessions.find(s => s.expiry > now);
|
|
57
|
+
if (!valid)
|
|
58
|
+
return null;
|
|
59
|
+
// Auto-extend if expiring within 2 days
|
|
60
|
+
const twoDays = 2 * 24 * 60 * 60;
|
|
61
|
+
if (valid.expiry - now < twoDays) {
|
|
62
|
+
try {
|
|
63
|
+
await client.extend({ topic: valid.topic });
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
// Extension failed — session might be dead
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
// Verify connectivity
|
|
70
|
+
try {
|
|
71
|
+
await client.ping({ topic: valid.topic });
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
return null; // session dead — user needs to re-scan
|
|
75
|
+
}
|
|
76
|
+
// Extract account address
|
|
77
|
+
const accounts = valid.namespaces.eip155?.accounts || [];
|
|
78
|
+
const account = accounts.length > 0 ? accounts[0].split(':').pop() : '';
|
|
79
|
+
return { client, topic: valid.topic, account };
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Connect a new wallet via QR code scan.
|
|
87
|
+
* Session persists automatically via SQLite storage.
|
|
88
|
+
*/
|
|
89
|
+
export async function connectWallet(onQRCode, onStatus) {
|
|
90
|
+
onStatus('Initializing WalletConnect...');
|
|
91
|
+
const client = await getSignClient();
|
|
92
|
+
const { uri, approval } = await client.connect({
|
|
93
|
+
requiredNamespaces: {
|
|
94
|
+
eip155: {
|
|
95
|
+
methods: ['eth_signTypedData_v4', 'eth_signTypedData', 'eth_sendTransaction'],
|
|
96
|
+
chains: ['eip155:42161'], // Arbitrum
|
|
97
|
+
events: ['accountsChanged', 'chainChanged'],
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
if (!uri) {
|
|
102
|
+
throw new Error('Failed to generate WalletConnect URI');
|
|
103
|
+
}
|
|
104
|
+
onQRCode(uri);
|
|
105
|
+
const session = await approval();
|
|
106
|
+
const accounts = session.namespaces.eip155?.accounts || [];
|
|
107
|
+
if (accounts.length === 0) {
|
|
108
|
+
throw new Error('No accounts returned from wallet');
|
|
109
|
+
}
|
|
110
|
+
const account = accounts[0].split(':').pop();
|
|
111
|
+
return { client, topic: session.topic, account };
|
|
112
|
+
}
|
|
113
|
+
// ─── SIGNING REQUESTS ──────────────────────────────────────
|
|
114
|
+
/**
|
|
115
|
+
* Request an EIP-712 signature via WalletConnect (persisted session).
|
|
116
|
+
* Sends push notification to user's phone. Returns null if no session.
|
|
117
|
+
*/
|
|
118
|
+
export async function requestSignature(typedData) {
|
|
119
|
+
const session = await getExistingSession();
|
|
120
|
+
if (!session)
|
|
121
|
+
return null;
|
|
122
|
+
try {
|
|
123
|
+
const result = await session.client.request({
|
|
124
|
+
topic: session.topic,
|
|
125
|
+
chainId: 'eip155:42161',
|
|
126
|
+
request: {
|
|
127
|
+
method: 'eth_signTypedData_v4',
|
|
128
|
+
params: [session.account, JSON.stringify(typedData)],
|
|
129
|
+
},
|
|
130
|
+
});
|
|
131
|
+
return result;
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Request ApproveAgent signature via WalletConnect.
|
|
139
|
+
*/
|
|
140
|
+
export async function requestAgentApproval(session, agentAddress, isMainnet = true) {
|
|
141
|
+
const nonce = Date.now();
|
|
142
|
+
const typedData = buildApproveAgentTypedData(agentAddress, nonce, isMainnet);
|
|
143
|
+
const action = {
|
|
144
|
+
type: 'approveAgent',
|
|
145
|
+
hyperliquidChain: isMainnet ? 'Mainnet' : 'Testnet',
|
|
146
|
+
signatureChainId: HL_SIGNATURE_CHAIN_ID,
|
|
147
|
+
agentAddress,
|
|
148
|
+
agentName: '',
|
|
149
|
+
nonce,
|
|
150
|
+
};
|
|
151
|
+
try {
|
|
152
|
+
const signature = await session.client.request({
|
|
153
|
+
topic: session.topic,
|
|
154
|
+
chainId: 'eip155:42161',
|
|
155
|
+
request: {
|
|
156
|
+
method: 'eth_signTypedData_v4',
|
|
157
|
+
params: [session.account, JSON.stringify(typedData)],
|
|
158
|
+
},
|
|
159
|
+
});
|
|
160
|
+
return { success: true, signature, nonce, action };
|
|
161
|
+
}
|
|
162
|
+
catch (error) {
|
|
163
|
+
return { success: false, error: error?.message || 'User rejected signature' };
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Request ApproveBuilderFee signature via WalletConnect.
|
|
168
|
+
*/
|
|
169
|
+
export async function requestBuilderFeeApproval(session, isMainnet = true) {
|
|
170
|
+
const nonce = Date.now();
|
|
171
|
+
const typedData = buildApproveBuilderFeeTypedData(nonce, isMainnet);
|
|
172
|
+
const action = {
|
|
173
|
+
type: 'approveBuilderFee',
|
|
174
|
+
hyperliquidChain: isMainnet ? 'Mainnet' : 'Testnet',
|
|
175
|
+
signatureChainId: HL_SIGNATURE_CHAIN_ID,
|
|
176
|
+
maxFeeRate: BUILDER_FEE_RATE,
|
|
177
|
+
builder: BUILDER_ADDRESS,
|
|
178
|
+
nonce,
|
|
179
|
+
};
|
|
180
|
+
try {
|
|
181
|
+
const signature = await session.client.request({
|
|
182
|
+
topic: session.topic,
|
|
183
|
+
chainId: 'eip155:42161',
|
|
184
|
+
request: {
|
|
185
|
+
method: 'eth_signTypedData_v4',
|
|
186
|
+
params: [session.account, JSON.stringify(typedData)],
|
|
187
|
+
},
|
|
188
|
+
});
|
|
189
|
+
return { success: true, signature, nonce, action };
|
|
190
|
+
}
|
|
191
|
+
catch (error) {
|
|
192
|
+
return { success: false, error: error?.message || 'User rejected signature' };
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
// ─── FUND MANAGEMENT SIGNING ───────────────────────────────
|
|
196
|
+
/**
|
|
197
|
+
* Request withdrawal signature via WalletConnect.
|
|
198
|
+
* User approves on phone, USDC bridges from HL to Arbitrum.
|
|
199
|
+
*/
|
|
200
|
+
export async function requestWithdrawal(amount, destination, isMainnet = true) {
|
|
201
|
+
const nonce = Date.now();
|
|
202
|
+
const typedData = buildWithdrawTypedData(destination, amount, nonce, isMainnet);
|
|
203
|
+
const action = {
|
|
204
|
+
type: 'withdraw3',
|
|
205
|
+
hyperliquidChain: isMainnet ? 'Mainnet' : 'Testnet',
|
|
206
|
+
signatureChainId: HL_SIGNATURE_CHAIN_ID,
|
|
207
|
+
destination,
|
|
208
|
+
amount,
|
|
209
|
+
time: nonce,
|
|
210
|
+
};
|
|
211
|
+
const session = await getExistingSession();
|
|
212
|
+
if (!session) {
|
|
213
|
+
return { success: false, error: 'No wallet connected. Run: rift auth setup' };
|
|
214
|
+
}
|
|
215
|
+
try {
|
|
216
|
+
const signature = await session.client.request({
|
|
217
|
+
topic: session.topic,
|
|
218
|
+
chainId: 'eip155:42161',
|
|
219
|
+
request: {
|
|
220
|
+
method: 'eth_signTypedData_v4',
|
|
221
|
+
params: [session.account, JSON.stringify(typedData)],
|
|
222
|
+
},
|
|
223
|
+
});
|
|
224
|
+
return { success: true, signature, nonce, action };
|
|
225
|
+
}
|
|
226
|
+
catch (error) {
|
|
227
|
+
return { success: false, error: error?.message || 'User rejected withdrawal' };
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Request spot↔perps transfer signature via WalletConnect.
|
|
232
|
+
*/
|
|
233
|
+
export async function requestTransfer(amount, toPerp, isMainnet = true) {
|
|
234
|
+
const nonce = Date.now();
|
|
235
|
+
const typedData = buildTransferTypedData(amount, toPerp, nonce, isMainnet);
|
|
236
|
+
const action = {
|
|
237
|
+
type: 'usdClassTransfer',
|
|
238
|
+
hyperliquidChain: isMainnet ? 'Mainnet' : 'Testnet',
|
|
239
|
+
signatureChainId: HL_SIGNATURE_CHAIN_ID,
|
|
240
|
+
amount,
|
|
241
|
+
toPerp,
|
|
242
|
+
nonce,
|
|
243
|
+
};
|
|
244
|
+
const session = await getExistingSession();
|
|
245
|
+
if (!session) {
|
|
246
|
+
return { success: false, error: 'No wallet connected. Run: rift auth setup' };
|
|
247
|
+
}
|
|
248
|
+
try {
|
|
249
|
+
const signature = await session.client.request({
|
|
250
|
+
topic: session.topic,
|
|
251
|
+
chainId: 'eip155:42161',
|
|
252
|
+
request: {
|
|
253
|
+
method: 'eth_signTypedData_v4',
|
|
254
|
+
params: [session.account, JSON.stringify(typedData)],
|
|
255
|
+
},
|
|
256
|
+
});
|
|
257
|
+
return { success: true, signature, nonce, action };
|
|
258
|
+
}
|
|
259
|
+
catch (error) {
|
|
260
|
+
return { success: false, error: error?.message || 'User rejected transfer' };
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
// ─── POST TO HYPERLIQUID ───────────────────────────────────
|
|
264
|
+
/**
|
|
265
|
+
* Post a signed action to Hyperliquid's exchange API.
|
|
266
|
+
*/
|
|
267
|
+
export async function postToHyperliquid(action, signature, nonce, isMainnet = true) {
|
|
268
|
+
const baseUrl = HL_MAINNET_API;
|
|
269
|
+
// Parse signature into r, s, v
|
|
270
|
+
const sigBytes = signature.startsWith('0x') ? signature.slice(2) : signature;
|
|
271
|
+
const r = '0x' + sigBytes.slice(0, 64);
|
|
272
|
+
const s = '0x' + sigBytes.slice(64, 128);
|
|
273
|
+
const v = parseInt(sigBytes.slice(128, 130), 16);
|
|
274
|
+
const response = await fetch(`${baseUrl}/exchange`, {
|
|
275
|
+
method: 'POST',
|
|
276
|
+
headers: { 'Content-Type': 'application/json' },
|
|
277
|
+
body: JSON.stringify({
|
|
278
|
+
action,
|
|
279
|
+
nonce,
|
|
280
|
+
signature: { r, s, v },
|
|
281
|
+
}),
|
|
282
|
+
});
|
|
283
|
+
if (!response.ok) {
|
|
284
|
+
const text = await response.text();
|
|
285
|
+
throw new Error(`Hyperliquid API error: ${response.status} — ${text}`);
|
|
286
|
+
}
|
|
287
|
+
return response.json();
|
|
288
|
+
}
|
|
289
|
+
// Legacy alias
|
|
290
|
+
export const postApprovalToHyperliquid = postToHyperliquid;
|
|
291
|
+
/**
|
|
292
|
+
* Display QR code in terminal.
|
|
293
|
+
*/
|
|
294
|
+
export function showQRCode(uri) {
|
|
295
|
+
qrcode.generate(uri, { small: true });
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* Disconnect WalletConnect session (only if user explicitly requests).
|
|
299
|
+
*/
|
|
300
|
+
export async function disconnectWallet(session) {
|
|
301
|
+
try {
|
|
302
|
+
await session.client.disconnect({
|
|
303
|
+
topic: session.topic,
|
|
304
|
+
reason: { code: 6000, message: 'User disconnected' },
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
catch {
|
|
308
|
+
// Ignore disconnect errors
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
// ─── EIP-712 TYPE BUILDERS ─────────────────────────────────
|
|
312
|
+
const HL_DOMAIN = {
|
|
313
|
+
name: 'HyperliquidSignTransaction',
|
|
314
|
+
version: '1',
|
|
315
|
+
chainId: HL_SIGNATURE_CHAIN_ID_DECIMAL,
|
|
316
|
+
verifyingContract: '0x0000000000000000000000000000000000000000',
|
|
317
|
+
};
|
|
318
|
+
const EIP712_DOMAIN_TYPE = [
|
|
319
|
+
{ name: 'name', type: 'string' },
|
|
320
|
+
{ name: 'version', type: 'string' },
|
|
321
|
+
{ name: 'chainId', type: 'uint256' },
|
|
322
|
+
{ name: 'verifyingContract', type: 'address' },
|
|
323
|
+
];
|
|
324
|
+
function buildApproveAgentTypedData(agentAddress, nonce, isMainnet) {
|
|
325
|
+
return {
|
|
326
|
+
domain: HL_DOMAIN,
|
|
327
|
+
types: {
|
|
328
|
+
EIP712Domain: EIP712_DOMAIN_TYPE,
|
|
329
|
+
'HyperliquidTransaction:ApproveAgent': [
|
|
330
|
+
{ name: 'hyperliquidChain', type: 'string' },
|
|
331
|
+
{ name: 'agentAddress', type: 'address' },
|
|
332
|
+
{ name: 'agentName', type: 'string' },
|
|
333
|
+
{ name: 'nonce', type: 'uint64' },
|
|
334
|
+
],
|
|
335
|
+
},
|
|
336
|
+
primaryType: 'HyperliquidTransaction:ApproveAgent',
|
|
337
|
+
message: {
|
|
338
|
+
hyperliquidChain: isMainnet ? 'Mainnet' : 'Testnet',
|
|
339
|
+
agentAddress,
|
|
340
|
+
agentName: '',
|
|
341
|
+
nonce,
|
|
342
|
+
},
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
function buildApproveBuilderFeeTypedData(nonce, isMainnet) {
|
|
346
|
+
return {
|
|
347
|
+
domain: HL_DOMAIN,
|
|
348
|
+
types: {
|
|
349
|
+
EIP712Domain: EIP712_DOMAIN_TYPE,
|
|
350
|
+
'HyperliquidTransaction:ApproveBuilderFee': [
|
|
351
|
+
{ name: 'hyperliquidChain', type: 'string' },
|
|
352
|
+
{ name: 'maxFeeRate', type: 'string' },
|
|
353
|
+
{ name: 'builder', type: 'address' },
|
|
354
|
+
{ name: 'nonce', type: 'uint64' },
|
|
355
|
+
],
|
|
356
|
+
},
|
|
357
|
+
primaryType: 'HyperliquidTransaction:ApproveBuilderFee',
|
|
358
|
+
message: {
|
|
359
|
+
hyperliquidChain: isMainnet ? 'Mainnet' : 'Testnet',
|
|
360
|
+
maxFeeRate: BUILDER_FEE_RATE,
|
|
361
|
+
builder: BUILDER_ADDRESS,
|
|
362
|
+
nonce,
|
|
363
|
+
},
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
function buildWithdrawTypedData(destination, amount, nonce, isMainnet) {
|
|
367
|
+
return {
|
|
368
|
+
domain: HL_DOMAIN,
|
|
369
|
+
types: {
|
|
370
|
+
EIP712Domain: EIP712_DOMAIN_TYPE,
|
|
371
|
+
'HyperliquidTransaction:Withdraw': [
|
|
372
|
+
{ name: 'hyperliquidChain', type: 'string' },
|
|
373
|
+
{ name: 'destination', type: 'string' },
|
|
374
|
+
{ name: 'amount', type: 'string' },
|
|
375
|
+
{ name: 'time', type: 'uint64' },
|
|
376
|
+
],
|
|
377
|
+
},
|
|
378
|
+
primaryType: 'HyperliquidTransaction:Withdraw',
|
|
379
|
+
message: {
|
|
380
|
+
hyperliquidChain: isMainnet ? 'Mainnet' : 'Testnet',
|
|
381
|
+
destination,
|
|
382
|
+
amount,
|
|
383
|
+
time: nonce,
|
|
384
|
+
},
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
function buildTransferTypedData(amount, toPerp, nonce, isMainnet) {
|
|
388
|
+
return {
|
|
389
|
+
domain: HL_DOMAIN,
|
|
390
|
+
types: {
|
|
391
|
+
EIP712Domain: EIP712_DOMAIN_TYPE,
|
|
392
|
+
'HyperliquidTransaction:UsdClassTransfer': [
|
|
393
|
+
{ name: 'hyperliquidChain', type: 'string' },
|
|
394
|
+
{ name: 'amount', type: 'string' },
|
|
395
|
+
{ name: 'toPerp', type: 'bool' },
|
|
396
|
+
{ name: 'nonce', type: 'uint64' },
|
|
397
|
+
],
|
|
398
|
+
},
|
|
399
|
+
primaryType: 'HyperliquidTransaction:UsdClassTransfer',
|
|
400
|
+
message: {
|
|
401
|
+
hyperliquidChain: isMainnet ? 'Mainnet' : 'Testnet',
|
|
402
|
+
amount,
|
|
403
|
+
toPerp,
|
|
404
|
+
nonce,
|
|
405
|
+
},
|
|
406
|
+
};
|
|
407
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@nexstone/rift-cli",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "RIFT — Research / Iteration / Forecast / Trade",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "dist/index.js",
|
|
8
|
+
"bin": {
|
|
9
|
+
"rift": "./bin/run.js"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"dist",
|
|
13
|
+
"bin"
|
|
14
|
+
],
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"@anthropic-ai/sdk": "^0.91.1",
|
|
17
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
18
|
+
"@oclif/core": "^4",
|
|
19
|
+
"@oclif/plugin-help": "^6",
|
|
20
|
+
"@walletconnect/sign-client": "^2.23.9",
|
|
21
|
+
"@walletconnect/types": "^2.23.9",
|
|
22
|
+
"chalk": "^5",
|
|
23
|
+
"qrcode-terminal": "^0.12.0",
|
|
24
|
+
"viem": "^2",
|
|
25
|
+
"zod": "^3.25.76"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@types/node": "^22",
|
|
29
|
+
"typescript": "^5.7"
|
|
30
|
+
},
|
|
31
|
+
"oclif": {
|
|
32
|
+
"bin": "rift",
|
|
33
|
+
"dirname": "rift",
|
|
34
|
+
"commands": "./dist/commands",
|
|
35
|
+
"default": "home",
|
|
36
|
+
"plugins": [
|
|
37
|
+
"@oclif/plugin-help"
|
|
38
|
+
],
|
|
39
|
+
"topicSeparator": " "
|
|
40
|
+
},
|
|
41
|
+
"engines": {
|
|
42
|
+
"node": ">=20"
|
|
43
|
+
},
|
|
44
|
+
"scripts": {
|
|
45
|
+
"build": "tsc",
|
|
46
|
+
"dev": "tsc --watch",
|
|
47
|
+
"lint": "tsc --noEmit"
|
|
48
|
+
}
|
|
49
|
+
}
|