Skip to content

Commit 9489af9

Browse files
committed
Merge branch 'dev'
2 parents f1ea626 + a00c79a commit 9489af9

File tree

12 files changed

+1215
-575
lines changed

12 files changed

+1215
-575
lines changed

bun.lock

Lines changed: 570 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package-lock.json

Lines changed: 29 additions & 572 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/config.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import { privateKeyToAccount } from "viem/accounts";
99
import dotenv from "dotenv";
1010
import { privateKeySchema } from "./tools/hyper-evm/sendFunds/schemas.js";
1111
import * as hyper from "@nktkas/hyperliquid";
12-
1312
dotenv.config();
1413

1514
export const hyperEvmConfig = defineChain({

src/main.ts

Lines changed: 80 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,28 +5,36 @@ import {
55
ListToolsRequestSchema,
66
type CallToolRequest,
77
} from "@modelcontextprotocol/sdk/types.js";
8+
89
import {
910
GET_BALANCE_TOOL,
1011
GET_LATEST_BLOCK_TOOL,
1112
DEPLOY_CONTRACTS_TOOL,
1213
SEND_FUNDS_TOOL,
1314
GET_TRANSACTION_RECEIPT_TOOL,
1415
GET_TOKEN_BALANCE_TOOL,
16+
FETCH_TRANSACTIONS_TOOL,
17+
GET_LOGS_TOOL,
18+
CALL_CONTRACT_FUNCTION,
1519
STAKE_TOOL,
1620
UNSTAKE_TOOL,
17-
GET_LOGS_TOOL,
1821
GET_HISTORICAL_ORDERS_TOOL,
22+
TRACK_STAKED_TOKENS,
1923
} from "./tools/tools.js";
2024
import { getBalance } from "./tools/hyper-evm/getBalance/index.js";
2125
import { getLatestBlock } from "./tools/hyper-evm/getBlockNumber/index.js";
2226
import { deployContracts } from "./tools/hyper-evm/deployContracts/index.js";
2327
import type { DeployContractsInput } from "./tools/hyper-evm/deployContracts/schemas.js";
28+
import { callContracts } from "./tools/hyper-evm/callContracts/index.js";
29+
import { CallContractSchema } from "./tools/hyper-evm/callContracts/schema.js";
2430
import { sendFunds } from "./tools/hyper-evm/sendFunds/index.js";
2531
import { sendFundsInputSchema } from "./tools/hyper-evm/sendFunds/schemas.js";
2632
import { getTransactionReceipt } from "./tools/hyper-evm/getTransactionReceipt/index.js";
2733
import type { getTransactionReceiptInput } from "./tools/hyper-evm/getTransactionReceipt/schemas.js";
2834
import { getTokenBalanceInputSchema } from "./tools/hyper-evm/getTokenBalance/schemas.js";
2935
import { getTokenBalance } from "./tools/hyper-evm/getTokenBalance/index.js";
36+
import { fetchTransactions } from "./tools/hyper-evm/fetchTransactions/index.js";
37+
import type { FetchTransactionsInput } from "./tools/hyper-evm/fetchTransactions/schemas.js";
3038
import {
3139
performStaking,
3240
performUnstaking,
@@ -37,9 +45,12 @@ import {
3745
} from "./tools/hyper-evm/handleStake/schemas.js";
3846
import { getLogs } from "./tools/hyper-evm/getLogs/index.js";
3947
import { getHistoricalOrders } from "./tools/hypercore/getHistoricalOrders/index.js";
48+
import { getStakedtokens } from "./tools/hypercore/trackstakedtokens/index.js";
49+
import { StakedInputSchema } from "./tools/hypercore/trackstakedtokens/schema.js";
4050

4151
async function main() {
4252
console.error("Starting Hyperliquid MCP server...");
53+
4354
const server = new Server(
4455
{
4556
name: "hyperliquid",
@@ -68,6 +79,54 @@ async function main() {
6879
return balance;
6980
}
7081

82+
case "call_contract_function": {
83+
try {
84+
const { contractAddress, functionName, abi, functionArgs } =
85+
args as {
86+
contractAddress: string;
87+
functionName: string;
88+
abi: any;
89+
functionArgs?: any[];
90+
};
91+
92+
const validatedInput = CallContractSchema.parse({
93+
contractAddress,
94+
functionName,
95+
abi,
96+
functionArgs,
97+
});
98+
99+
const result = await callContracts(validatedInput);
100+
101+
return {
102+
content: [
103+
{
104+
type: "text",
105+
text: JSON.stringify(
106+
result,
107+
(_, v) => (typeof v === "bigint" ? v.toString() : v),
108+
2
109+
),
110+
},
111+
],
112+
};
113+
} catch (validationError) {
114+
console.error("Validation error:", validationError);
115+
return {
116+
content: [
117+
{
118+
type: "text",
119+
text: `Error: ${
120+
validationError instanceof Error
121+
? validationError.message
122+
: String(validationError)
123+
}`,
124+
},
125+
],
126+
};
127+
}
128+
}
129+
71130
case "deploy_contracts": {
72131
const input = args as DeployContractsInput;
73132
const result = await deployContracts(input);
@@ -110,6 +169,12 @@ async function main() {
110169
return result;
111170
}
112171

172+
case "fetch_transactions": {
173+
const input = args as FetchTransactionsInput;
174+
const result = await fetchTransactions(input);
175+
return result;
176+
}
177+
113178
case "stake": {
114179
const input = args as {
115180
amountToStake: string;
@@ -151,9 +216,19 @@ async function main() {
151216
return result;
152217
}
153218

219+
case "track_staked_tokens": {
220+
const input = args as {
221+
userAddress: string;
222+
isTestnet: boolean | string;
223+
};
224+
const validatedInput = StakedInputSchema.parse(input);
225+
const result = await getStakedtokens(validatedInput);
226+
return result;
227+
}
228+
154229
default: {
155230
throw new Error(
156-
`Tool '${name}' not found. Available tools: get_latest_block, get_balance, deploy_contracts, send_funds, get_transaction_receipt, get_token_balance, stake, unstake`
231+
`Tool '${name}' not found. Available tools: get_latest_block, get_balance, deploy_contracts, send_funds, get_transaction_receipt, get_token_balance, stake, unstake, get_logs, call_contract_function, track_staked_tokens`
157232
);
158233
}
159234
}
@@ -181,10 +256,13 @@ async function main() {
181256
SEND_FUNDS_TOOL,
182257
GET_TRANSACTION_RECEIPT_TOOL,
183258
GET_TOKEN_BALANCE_TOOL,
259+
FETCH_TRANSACTIONS_TOOL,
260+
CALL_CONTRACT_FUNCTION,
184261
STAKE_TOOL,
185262
UNSTAKE_TOOL,
186263
GET_LOGS_TOOL,
187264
GET_HISTORICAL_ORDERS_TOOL,
265+
TRACK_STAKED_TOKENS,
188266
],
189267
};
190268
});
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import {
2+
encodeFunctionData,
3+
type Abi,
4+
type AbiFunction,
5+
isAddress,
6+
} from "viem";
7+
import { createWalletClient, http } from "viem";
8+
import { publicClient, walletClient } from "../../../config.js";
9+
import { hyperEvmConfig } from "../../../config.js";
10+
import type { GetContractDetails } from "./schema.js";
11+
import { privateKeySchema } from "../sendFunds/schemas.js";
12+
import { privateKeyToAccount } from "viem/accounts";
13+
14+
export async function callContracts(contractDetails: GetContractDetails) {
15+
try {
16+
const address = contractDetails.contractAddress;
17+
if (!isAddress(address)) {
18+
throw new Error(`Invalid HyperEVM address: ${address}`);
19+
}
20+
21+
let abi: Abi;
22+
if (typeof contractDetails.abi === "string") {
23+
try {
24+
abi = JSON.parse(contractDetails.abi) as Abi;
25+
} catch (error) {
26+
throw new Error(`Invalid ABI string: ${error}`);
27+
}
28+
} else {
29+
abi = contractDetails.abi as Abi;
30+
}
31+
32+
const functionAbi = abi.find(
33+
(item): item is AbiFunction =>
34+
"type" in item &&
35+
item.type === "function" &&
36+
"name" in item &&
37+
item.name === contractDetails.functionName
38+
);
39+
40+
if (!functionAbi) {
41+
throw new Error(
42+
`Function ${contractDetails.functionName} not found in ABI`
43+
);
44+
}
45+
46+
if (
47+
functionAbi.stateMutability === "view" ||
48+
functionAbi.stateMutability === "pure"
49+
) {
50+
const callParams: any = {
51+
address: contractDetails.contractAddress,
52+
abi,
53+
functionName: contractDetails.functionName,
54+
};
55+
56+
if (functionAbi.inputs && functionAbi.inputs.length > 0) {
57+
if (
58+
!contractDetails.functionArgs ||
59+
contractDetails.functionArgs.length !== functionAbi.inputs.length
60+
) {
61+
throw Error(
62+
`Function ${contractDetails.functionName} expects ${functionAbi.inputs.length} arguments, ` +
63+
`${contractDetails.functionArgs?.length || 0} provided`
64+
);
65+
}
66+
callParams.args = contractDetails.functionArgs;
67+
}
68+
69+
return await publicClient.readContract(callParams);
70+
}
71+
72+
const callParams: any = {
73+
address: contractDetails.contractAddress,
74+
abi,
75+
functionName: contractDetails.functionName,
76+
};
77+
78+
if (functionAbi.inputs && functionAbi.inputs.length > 0) {
79+
if (
80+
!contractDetails.functionArgs ||
81+
contractDetails.functionArgs.length !== functionAbi.inputs.length
82+
) {
83+
throw Error(
84+
`Function ${contractDetails.functionName} expects ${functionAbi.inputs.length} arguments, ` +
85+
`${contractDetails.functionArgs?.length || 0} provided`
86+
);
87+
}
88+
callParams.args = contractDetails.functionArgs;
89+
}
90+
91+
return await walletClient.writeContract(callParams);
92+
} catch (error) {
93+
console.log(error);
94+
throw Error(
95+
`Failed to call contract function: ${error instanceof Error ? error.message : String(error)}`
96+
);
97+
}
98+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { z } from "zod";
2+
import { isAddress } from "viem";
3+
4+
export const CallContractSchema = z.object({
5+
contractAddress: z.string().refine(address => isAddress(address), {
6+
message: "Must be a valid HyperEVM address (0x format, 42 characters)",
7+
}),
8+
functionName: z.string().min(1, "Function name cannot be empty"),
9+
abi: z.union([z.string(), z.array(z.any())]),
10+
functionArgs: z.preprocess(val => {
11+
if (typeof val === "string") {
12+
try {
13+
return JSON.parse(val);
14+
} catch {
15+
return [val];
16+
}
17+
}
18+
return val;
19+
}, z.array(z.any()).optional()),
20+
});
21+
22+
export type GetContractDetails = z.infer<typeof CallContractSchema>;

0 commit comments

Comments
 (0)