safehands-pharos 1.4.0 → 1.5.0

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.
Files changed (124) hide show
  1. package/README.md +174 -63
  2. package/dist/cli.d.ts +5 -5
  3. package/dist/cli.d.ts.map +1 -1
  4. package/dist/cli.js +126 -124
  5. package/dist/cli.js.map +1 -1
  6. package/dist/demo.d.ts +1 -1
  7. package/dist/demo.js +171 -171
  8. package/dist/index.d.ts +2 -2
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js +73 -65
  11. package/dist/index.js.map +1 -1
  12. package/dist/init.d.ts +1 -1
  13. package/dist/init.js +65 -65
  14. package/dist/lib/constants.d.ts +303 -291
  15. package/dist/lib/constants.d.ts.map +1 -1
  16. package/dist/lib/constants.js +302 -292
  17. package/dist/lib/constants.js.map +1 -1
  18. package/dist/lib/dodoApi.d.ts +78 -78
  19. package/dist/lib/dodoApi.js +196 -196
  20. package/dist/lib/envLoader.d.ts +2 -0
  21. package/dist/lib/envLoader.d.ts.map +1 -0
  22. package/dist/lib/envLoader.js +44 -0
  23. package/dist/lib/envLoader.js.map +1 -0
  24. package/dist/lib/http.d.ts +14 -14
  25. package/dist/lib/http.js +118 -118
  26. package/dist/lib/pharosClient.d.ts +58 -58
  27. package/dist/lib/pharosClient.js +63 -63
  28. package/dist/lib/policy/actionPolicyEngine.d.ts +53 -53
  29. package/dist/lib/policy/actionPolicyEngine.js +212 -212
  30. package/dist/lib/riskEngine.d.ts +26 -26
  31. package/dist/lib/riskEngine.d.ts.map +1 -1
  32. package/dist/lib/riskEngine.js +288 -283
  33. package/dist/lib/riskEngine.js.map +1 -1
  34. package/dist/lib/signer/index.d.ts +24 -24
  35. package/dist/lib/signer/index.js +88 -88
  36. package/dist/lib/testDodoLive.d.ts +1 -1
  37. package/dist/lib/testDodoLive.js +104 -104
  38. package/dist/lib/testLiveSafehands.d.ts +1 -1
  39. package/dist/lib/testLiveSafehands.js +92 -92
  40. package/dist/lib/testRpcLive.d.ts +1 -1
  41. package/dist/lib/testRpcLive.js +88 -88
  42. package/dist/lib/testTools.js +397 -398
  43. package/dist/lib/testX402Live.d.ts +1 -1
  44. package/dist/lib/testX402Live.js +159 -159
  45. package/dist/lib/toolResponse.d.ts +25 -25
  46. package/dist/lib/toolResponse.js +53 -53
  47. package/dist/lib/wallet/index.d.ts +37 -37
  48. package/dist/lib/wallet/index.js +128 -128
  49. package/dist/scripts/checkDeploy.d.ts +1 -1
  50. package/dist/scripts/checkDeploy.js +24 -24
  51. package/dist/scripts/deployRegistry.d.ts +1 -1
  52. package/dist/scripts/deployRegistry.js +100 -100
  53. package/dist/scripts/testRegistry.d.ts +1 -1
  54. package/dist/scripts/testRegistry.js +43 -43
  55. package/dist/tools/approveToken.d.ts +45 -45
  56. package/dist/tools/approveToken.js +85 -85
  57. package/dist/tools/assessRisk.d.ts +79 -79
  58. package/dist/tools/assessRisk.js +104 -104
  59. package/dist/tools/checkAllowance.d.ts +43 -43
  60. package/dist/tools/checkAllowance.js +56 -56
  61. package/dist/tools/checkTokenSecurity.d.ts +46 -46
  62. package/dist/tools/checkTokenSecurity.js +95 -95
  63. package/dist/tools/createAgentWallet.d.ts +28 -26
  64. package/dist/tools/createAgentWallet.d.ts.map +1 -1
  65. package/dist/tools/createAgentWallet.js +82 -58
  66. package/dist/tools/createAgentWallet.js.map +1 -1
  67. package/dist/tools/estimateGas.d.ts +79 -79
  68. package/dist/tools/estimateGas.js +124 -124
  69. package/dist/tools/executeSwap.d.ts +61 -61
  70. package/dist/tools/executeSwap.js +141 -141
  71. package/dist/tools/explainRisk.d.ts +29 -29
  72. package/dist/tools/explainRisk.js +32 -32
  73. package/dist/tools/getAgentWallet.d.ts +21 -21
  74. package/dist/tools/getAgentWallet.js +27 -27
  75. package/dist/tools/getAgentWalletBalance.d.ts +11 -11
  76. package/dist/tools/getAgentWalletBalance.js +70 -70
  77. package/dist/tools/getExecutionHistory.d.ts +49 -49
  78. package/dist/tools/getExecutionHistory.js +154 -154
  79. package/dist/tools/getGasPrice.d.ts +43 -43
  80. package/dist/tools/getGasPrice.js +59 -59
  81. package/dist/tools/getPoolInfo.d.ts +75 -75
  82. package/dist/tools/getPoolInfo.js +137 -137
  83. package/dist/tools/getTokenPrice.d.ts +113 -113
  84. package/dist/tools/getTokenPrice.js +117 -117
  85. package/dist/tools/getTransactionStatus.d.ts +43 -43
  86. package/dist/tools/getTransactionStatus.js +59 -59
  87. package/dist/tools/getWalletBalance.d.ts +68 -68
  88. package/dist/tools/getWalletBalance.js +87 -87
  89. package/dist/tools/publishRiskScore.d.ts +63 -63
  90. package/dist/tools/publishRiskScore.js +88 -88
  91. package/dist/tools/queryRiskRegistry.d.ts +38 -38
  92. package/dist/tools/queryRiskRegistry.js +55 -55
  93. package/dist/tools/safehandsPreflightCheck.d.ts +77 -77
  94. package/dist/tools/safehandsPreflightCheck.js +47 -47
  95. package/dist/tools/safehandsRiskReport.d.ts +81 -81
  96. package/dist/tools/safehandsRiskReport.js +28 -28
  97. package/dist/tools/safehandsSafeExecute.d.ts +20 -20
  98. package/dist/tools/safehandsSafeExecute.js +81 -81
  99. package/dist/tools/safehandsWalletHealth.d.ts +14 -14
  100. package/dist/tools/safehandsWalletHealth.js +103 -103
  101. package/dist/tools/safehandsX402Preflight.d.ts +26 -26
  102. package/dist/tools/safehandsX402Preflight.js +65 -65
  103. package/dist/tools/sendPayment.d.ts +57 -57
  104. package/dist/tools/sendPayment.js +117 -117
  105. package/dist/tools/simulateTransaction.d.ts +60 -60
  106. package/dist/tools/simulateTransaction.js +83 -83
  107. package/dist/tools/tokenRegistryStatus.d.ts +26 -26
  108. package/dist/tools/tokenRegistryStatus.js +96 -96
  109. package/dist/tools/x402PayAndFetch.d.ts +81 -81
  110. package/dist/tools/x402PayAndFetch.js +152 -152
  111. package/dist/x402Server.d.ts +1 -1
  112. package/dist/x402Server.js +300 -252
  113. package/dist/x402Server.js.map +1 -1
  114. package/package.json +6 -16
  115. package/examples/dashboard/index.html +0 -337
  116. package/examples/pharos-skill-engine/SKILL.safehands.md +0 -85
  117. package/examples/pharos-skill-engine/assets/safehands/example-actions.json +0 -49
  118. package/examples/pharos-skill-engine/assets/safehands/policy-defaults.json +0 -11
  119. package/examples/pharos-skill-engine/references/safehands.md +0 -345
  120. package/examples/scenario-hack.ts +0 -38
  121. package/skill/SKILL.md +0 -133
  122. package/skill/assets/safehands/example-actions.json +0 -49
  123. package/skill/assets/safehands/policy-defaults.json +0 -11
  124. package/skill/references/safehands.md +0 -345
@@ -1,12 +1,12 @@
1
- import { z } from "zod";
2
- import { type ToolResponse } from "../lib/toolResponse.js";
3
- export declare const getAgentWalletBalanceSchema: z.ZodObject<{
4
- agentId: z.ZodString;
5
- }, "strip", z.ZodTypeAny, {
6
- agentId: string;
7
- }, {
8
- agentId: string;
9
- }>;
10
- export type GetAgentWalletBalanceParams = z.infer<typeof getAgentWalletBalanceSchema>;
11
- export declare function handleGetAgentWalletBalance(params: GetAgentWalletBalanceParams): Promise<ToolResponse<unknown>>;
1
+ import { z } from "zod";
2
+ import { type ToolResponse } from "../lib/toolResponse.js";
3
+ export declare const getAgentWalletBalanceSchema: z.ZodObject<{
4
+ agentId: z.ZodString;
5
+ }, "strip", z.ZodTypeAny, {
6
+ agentId: string;
7
+ }, {
8
+ agentId: string;
9
+ }>;
10
+ export type GetAgentWalletBalanceParams = z.infer<typeof getAgentWalletBalanceSchema>;
11
+ export declare function handleGetAgentWalletBalance(params: GetAgentWalletBalanceParams): Promise<ToolResponse<unknown>>;
12
12
  //# sourceMappingURL=getAgentWalletBalance.d.ts.map
@@ -1,71 +1,71 @@
1
- // ─── get_agent_wallet_balance ───────────────────────────────────────────
2
- // Returns PHRS + token balances for a managed agent wallet.
3
- // ────────────────────────────────────────────────────────────────────────
4
- import { z } from "zod";
5
- import { ok, fail, classifyExternalError } from "../lib/toolResponse.js";
6
- import { walletStore } from "../lib/wallet/index.js";
7
- import { publicClient } from "../lib/pharosClient.js";
8
- import { TOKEN_REGISTRY } from "../lib/constants.js";
9
- import { formatUnits } from "viem";
10
- export const getAgentWalletBalanceSchema = z.object({
11
- agentId: z.string().min(1).max(64).describe("Agent wallet identifier"),
12
- });
13
- const ERC20_ABI = [
14
- {
15
- name: "balanceOf",
16
- type: "function",
17
- stateMutability: "view",
18
- inputs: [{ name: "account", type: "address" }],
19
- outputs: [{ name: "", type: "uint256" }],
20
- },
21
- ];
22
- export async function handleGetAgentWalletBalance(params) {
23
- const { agentId } = params;
24
- const wallet = await walletStore.get(agentId);
25
- if (!wallet) {
26
- return fail("WALLET_NOT_FOUND", `No wallet found for agentId '${agentId}'. Use create_agent_wallet to create one.`, false, "wallet_store");
27
- }
28
- const address = wallet.address;
29
- try {
30
- const [phrsRaw, usdcRaw, usdtRaw] = await Promise.all([
31
- publicClient.getBalance({ address }),
32
- publicClient.readContract({
33
- address: TOKEN_REGISTRY.USDC.address,
34
- abi: ERC20_ABI,
35
- functionName: "balanceOf",
36
- args: [address],
37
- }),
38
- publicClient.readContract({
39
- address: TOKEN_REGISTRY.USDT.address,
40
- abi: ERC20_ABI,
41
- functionName: "balanceOf",
42
- args: [address],
43
- }),
44
- ]);
45
- const phrs = formatUnits(phrsRaw, 18);
46
- const usdc = formatUnits(usdcRaw, 6);
47
- const usdt = formatUnits(usdtRaw, 6);
48
- const isFunded = parseFloat(phrs) > 0;
49
- return ok({
50
- agentId,
51
- address,
52
- environment: wallet.environment,
53
- chainId: wallet.chainId,
54
- isMainnet: false,
55
- explorerUrl: `https://atlantic.pharosscan.xyz/address/${address}`,
56
- balances: {
57
- PHRS: { value: phrs, unit: "PHRS", decimals: 18 },
58
- USDC: { value: usdc, unit: "USDC", decimals: 6 },
59
- USDT: { value: usdt, unit: "USDT", decimals: 6 },
60
- },
61
- isFunded,
62
- fundingInstructions: isFunded
63
- ? null
64
- : `Wallet is unfunded. Get testnet PHRS from https://testnet.pharosnetwork.xyz/ — wallet address: ${address}`,
65
- });
66
- }
67
- catch (err) {
68
- return classifyExternalError("pharos_rpc", err);
69
- }
70
- }
1
+ // ─── get_agent_wallet_balance ───────────────────────────────────────────
2
+ // Returns PHRS + token balances for a managed agent wallet.
3
+ // ────────────────────────────────────────────────────────────────────────
4
+ import { z } from "zod";
5
+ import { ok, fail, classifyExternalError } from "../lib/toolResponse.js";
6
+ import { walletStore } from "../lib/wallet/index.js";
7
+ import { publicClient } from "../lib/pharosClient.js";
8
+ import { TOKEN_REGISTRY } from "../lib/constants.js";
9
+ import { formatUnits } from "viem";
10
+ export const getAgentWalletBalanceSchema = z.object({
11
+ agentId: z.string().min(1).max(64).describe("Agent wallet identifier"),
12
+ });
13
+ const ERC20_ABI = [
14
+ {
15
+ name: "balanceOf",
16
+ type: "function",
17
+ stateMutability: "view",
18
+ inputs: [{ name: "account", type: "address" }],
19
+ outputs: [{ name: "", type: "uint256" }],
20
+ },
21
+ ];
22
+ export async function handleGetAgentWalletBalance(params) {
23
+ const { agentId } = params;
24
+ const wallet = await walletStore.get(agentId);
25
+ if (!wallet) {
26
+ return fail("WALLET_NOT_FOUND", `No wallet found for agentId '${agentId}'. Use create_agent_wallet to create one.`, false, "wallet_store");
27
+ }
28
+ const address = wallet.address;
29
+ try {
30
+ const [phrsRaw, usdcRaw, usdtRaw] = await Promise.all([
31
+ publicClient.getBalance({ address }),
32
+ publicClient.readContract({
33
+ address: TOKEN_REGISTRY.USDC.address,
34
+ abi: ERC20_ABI,
35
+ functionName: "balanceOf",
36
+ args: [address],
37
+ }),
38
+ publicClient.readContract({
39
+ address: TOKEN_REGISTRY.USDT.address,
40
+ abi: ERC20_ABI,
41
+ functionName: "balanceOf",
42
+ args: [address],
43
+ }),
44
+ ]);
45
+ const phrs = formatUnits(phrsRaw, 18);
46
+ const usdc = formatUnits(usdcRaw, 6);
47
+ const usdt = formatUnits(usdtRaw, 6);
48
+ const isFunded = parseFloat(phrs) > 0;
49
+ return ok({
50
+ agentId,
51
+ address,
52
+ environment: wallet.environment,
53
+ chainId: wallet.chainId,
54
+ isMainnet: false,
55
+ explorerUrl: `https://atlantic.pharosscan.xyz/address/${address}`,
56
+ balances: {
57
+ PHRS: { value: phrs, unit: "PHRS", decimals: 18 },
58
+ USDC: { value: usdc, unit: "USDC", decimals: 6 },
59
+ USDT: { value: usdt, unit: "USDT", decimals: 6 },
60
+ },
61
+ isFunded,
62
+ fundingInstructions: isFunded
63
+ ? null
64
+ : `Wallet is unfunded. Get testnet PHRS from https://testnet.pharosnetwork.xyz/ — wallet address: ${address}`,
65
+ });
66
+ }
67
+ catch (err) {
68
+ return classifyExternalError("pharos_rpc", err);
69
+ }
70
+ }
71
71
  //# sourceMappingURL=getAgentWalletBalance.js.map
@@ -1,50 +1,50 @@
1
- import { z } from "zod";
2
- export declare const getExecutionHistorySchema: z.ZodObject<{
3
- walletAddress: z.ZodString;
4
- limit: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
5
- filter: z.ZodDefault<z.ZodOptional<z.ZodEnum<["swap", "transfer", "all"]>>>;
6
- }, "strip", z.ZodTypeAny, {
7
- filter: "transfer" | "all" | "swap";
8
- walletAddress: string;
9
- limit: number;
10
- }, {
11
- walletAddress: string;
12
- filter?: "transfer" | "all" | "swap" | undefined;
13
- limit?: number | undefined;
14
- }>;
15
- export type GetExecutionHistoryInput = z.infer<typeof getExecutionHistorySchema>;
16
- export declare const getExecutionHistoryTool: {
17
- name: string;
18
- description: string;
19
- inputSchema: z.ZodObject<{
20
- walletAddress: z.ZodString;
21
- limit: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
22
- filter: z.ZodDefault<z.ZodOptional<z.ZodEnum<["swap", "transfer", "all"]>>>;
23
- }, "strip", z.ZodTypeAny, {
24
- filter: "transfer" | "all" | "swap";
25
- walletAddress: string;
26
- limit: number;
27
- }, {
28
- walletAddress: string;
29
- filter?: "transfer" | "all" | "swap" | undefined;
30
- limit?: number | undefined;
31
- }>;
32
- };
33
- interface HistoryEntry {
34
- txHash: string;
35
- explorerUrl: string;
36
- type: "swap" | "transfer" | "other";
37
- timestamp: string;
38
- status: "success" | "failed" | "unknown";
39
- value: string;
40
- details: string;
41
- blockNumber: string;
42
- }
43
- export declare function handleGetExecutionHistory(input: GetExecutionHistoryInput): Promise<import("../lib/toolResponse.js").ToolFailure | import("../lib/toolResponse.js").ToolSuccess<{
44
- walletAddress: string;
45
- totalFetched: number;
46
- history: HistoryEntry[];
47
- note: string;
48
- }>>;
49
- export {};
1
+ import { z } from "zod";
2
+ export declare const getExecutionHistorySchema: z.ZodObject<{
3
+ walletAddress: z.ZodString;
4
+ limit: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
5
+ filter: z.ZodDefault<z.ZodOptional<z.ZodEnum<["swap", "transfer", "all"]>>>;
6
+ }, "strip", z.ZodTypeAny, {
7
+ filter: "transfer" | "all" | "swap";
8
+ walletAddress: string;
9
+ limit: number;
10
+ }, {
11
+ walletAddress: string;
12
+ filter?: "transfer" | "all" | "swap" | undefined;
13
+ limit?: number | undefined;
14
+ }>;
15
+ export type GetExecutionHistoryInput = z.infer<typeof getExecutionHistorySchema>;
16
+ export declare const getExecutionHistoryTool: {
17
+ name: string;
18
+ description: string;
19
+ inputSchema: z.ZodObject<{
20
+ walletAddress: z.ZodString;
21
+ limit: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
22
+ filter: z.ZodDefault<z.ZodOptional<z.ZodEnum<["swap", "transfer", "all"]>>>;
23
+ }, "strip", z.ZodTypeAny, {
24
+ filter: "transfer" | "all" | "swap";
25
+ walletAddress: string;
26
+ limit: number;
27
+ }, {
28
+ walletAddress: string;
29
+ filter?: "transfer" | "all" | "swap" | undefined;
30
+ limit?: number | undefined;
31
+ }>;
32
+ };
33
+ interface HistoryEntry {
34
+ txHash: string;
35
+ explorerUrl: string;
36
+ type: "swap" | "transfer" | "other";
37
+ timestamp: string;
38
+ status: "success" | "failed" | "unknown";
39
+ value: string;
40
+ details: string;
41
+ blockNumber: string;
42
+ }
43
+ export declare function handleGetExecutionHistory(input: GetExecutionHistoryInput): Promise<import("../lib/toolResponse.js").ToolFailure | import("../lib/toolResponse.js").ToolSuccess<{
44
+ walletAddress: string;
45
+ totalFetched: number;
46
+ history: HistoryEntry[];
47
+ note: string;
48
+ }>>;
49
+ export {};
50
50
  //# sourceMappingURL=getExecutionHistory.d.ts.map
@@ -1,155 +1,155 @@
1
- // ─── Tool: get_execution_history ───────────────────────────────────────
2
- // Uses eth_getLogs for ERC-20 Transfer events (fast, covers token swaps)
3
- // and a parallelised block scan of the last 100 blocks for native PHRS transfers.
4
- // Prior approach scanned 500 blocks sequentially — 500+ RPC calls, always timed out.
5
- // ────────────────────────────────────────────────────────────────────────
6
- import { z } from "zod";
7
- import { publicClient, getExplorerUrl } from "../lib/pharosClient.js";
8
- import { DODO_ROUTE_PROXY_ADDRESS, USDC_ADDRESS, USDT_ADDRESS, WBTC_ADDRESS, WETH_ADDRESS, WPHRS_ADDRESS, CIRCLE_USDC_ADDRESS, } from "../lib/constants.js";
9
- import { formatEther, parseAbiItem } from "viem";
10
- import { ok, fail } from "../lib/toolResponse.js";
11
- const TRANSFER_EVENT = parseAbiItem("event Transfer(address indexed from, address indexed to, uint256 value)");
12
- export const getExecutionHistorySchema = z.object({
13
- walletAddress: z.string(),
14
- limit: z.number().optional().default(20),
15
- filter: z.enum(["swap", "transfer", "all"]).optional().default("all"),
16
- });
17
- // All known token addresses to scan for Transfer events (mutable array for viem compatibility)
18
- const KNOWN_TOKEN_ADDRESSES = [
19
- USDC_ADDRESS,
20
- USDT_ADDRESS,
21
- WBTC_ADDRESS,
22
- WETH_ADDRESS,
23
- WPHRS_ADDRESS,
24
- CIRCLE_USDC_ADDRESS,
25
- ];
26
- const ERC20_LOG_RANGE = 10000n; // blocks to scan for ERC-20 events
27
- const NATIVE_SCAN_RANGE = 100n; // blocks for native PHRS scan
28
- const NATIVE_BATCH_SIZE = 10; // parallel block fetches per batch
29
- export const getExecutionHistoryTool = {
30
- name: "get_execution_history",
31
- description: "Pull on-chain transaction history for a wallet. Uses eth_getLogs for ERC-20 transfers and parallel block scan for native PHRS. Filters by swap, transfer, or all.",
32
- inputSchema: getExecutionHistorySchema,
33
- };
34
- export async function handleGetExecutionHistory(input) {
35
- const seen = new Set();
36
- const history = [];
37
- try {
38
- const blockNumber = await publicClient.getBlockNumber();
39
- const erc20FromBlock = blockNumber > ERC20_LOG_RANGE ? blockNumber - ERC20_LOG_RANGE : 0n;
40
- const nativeFromBlock = blockNumber > NATIVE_SCAN_RANGE ? blockNumber - NATIVE_SCAN_RANGE : 0n;
41
- const walletAddr = input.walletAddress;
42
- // ── ERC-20 Transfer logs ─────────────────────────────────────────────
43
- // Two queries: wallet as sender (from) and wallet as receiver (to)
44
- const empty = [];
45
- const [sentLogs, receivedLogs] = await Promise.all([
46
- publicClient.getLogs({
47
- fromBlock: erc20FromBlock,
48
- toBlock: blockNumber,
49
- address: KNOWN_TOKEN_ADDRESSES,
50
- event: TRANSFER_EVENT,
51
- args: { from: walletAddr },
52
- strict: true,
53
- }).catch(() => empty),
54
- publicClient.getLogs({
55
- fromBlock: erc20FromBlock,
56
- toBlock: blockNumber,
57
- address: KNOWN_TOKEN_ADDRESSES,
58
- event: TRANSFER_EVENT,
59
- args: { to: walletAddr },
60
- strict: true,
61
- }).catch(() => empty),
62
- ]);
63
- const allLogs = [...sentLogs, ...receivedLogs];
64
- // Resolve block timestamps in bulk for unique block numbers in logs
65
- const uniqueBlocks = [...new Set(allLogs.map((l) => l.blockNumber))];
66
- const blockTimestampMap = new Map();
67
- for (let i = 0; i < uniqueBlocks.length; i += NATIVE_BATCH_SIZE) {
68
- const batch = uniqueBlocks.slice(i, i + NATIVE_BATCH_SIZE);
69
- const blocks = await Promise.all(batch.map((bn) => publicClient.getBlock({ blockNumber: bn }).catch(() => null)));
70
- for (const block of blocks) {
71
- if (block) {
72
- blockTimestampMap.set(block.number, new Date(Number(block.timestamp) * 1000).toISOString());
73
- }
74
- }
75
- }
76
- for (const log of allLogs) {
77
- if (history.length >= input.limit)
78
- break;
79
- if (!log.transactionHash || seen.has(log.transactionHash))
80
- continue;
81
- const isDodoSwap = log.address.toLowerCase() === DODO_ROUTE_PROXY_ADDRESS.toLowerCase()
82
- || sentLogs.some((l) => l.transactionHash === log.transactionHash && l.address.toLowerCase() === DODO_ROUTE_PROXY_ADDRESS.toLowerCase())
83
- || receivedLogs.some((l) => l.transactionHash === log.transactionHash && l.address.toLowerCase() === DODO_ROUTE_PROXY_ADDRESS.toLowerCase());
84
- const txType = isDodoSwap ? "swap" : "transfer";
85
- if (input.filter !== "all" && txType !== input.filter)
86
- continue;
87
- seen.add(log.transactionHash);
88
- history.push({
89
- txHash: log.transactionHash,
90
- explorerUrl: getExplorerUrl(log.transactionHash),
91
- type: txType,
92
- timestamp: blockTimestampMap.get(log.blockNumber) ?? "unknown",
93
- status: "unknown", // receipt not fetched per-log to avoid N+1
94
- value: "0",
95
- details: `ERC-20 Transfer on block ${log.blockNumber.toString()}`,
96
- blockNumber: log.blockNumber.toString(),
97
- });
98
- }
99
- // ── Native PHRS transfers — parallel block scan (last 100 blocks) ───
100
- if (input.filter !== "swap" && history.length < input.limit) {
101
- const blockNums = [];
102
- for (let b = blockNumber; b > nativeFromBlock; b--) {
103
- blockNums.push(b);
104
- }
105
- for (let i = 0; i < blockNums.length && history.length < input.limit; i += NATIVE_BATCH_SIZE) {
106
- const batch = blockNums.slice(i, i + NATIVE_BATCH_SIZE);
107
- const blocks = await Promise.all(batch.map((bn) => publicClient.getBlock({ blockNumber: bn, includeTransactions: true }).catch(() => null)));
108
- for (const block of blocks) {
109
- if (!block)
110
- continue;
111
- const ts = new Date(Number(block.timestamp) * 1000).toISOString();
112
- for (const tx of block.transactions) {
113
- if (history.length >= input.limit)
114
- break;
115
- if (typeof tx === "string")
116
- continue;
117
- if (tx.value === 0n)
118
- continue; // no native transfer
119
- const isFrom = tx.from.toLowerCase() === input.walletAddress.toLowerCase();
120
- const isTo = tx.to?.toLowerCase() === input.walletAddress.toLowerCase();
121
- if (!isFrom && !isTo)
122
- continue;
123
- if (seen.has(tx.hash))
124
- continue;
125
- seen.add(tx.hash);
126
- history.push({
127
- txHash: tx.hash,
128
- explorerUrl: getExplorerUrl(tx.hash),
129
- type: "transfer",
130
- timestamp: ts,
131
- status: "unknown",
132
- value: formatEther(tx.value),
133
- details: isFrom
134
- ? `Sent ${formatEther(tx.value)} PHRS to ${tx.to}`
135
- : `Received ${formatEther(tx.value)} PHRS from ${tx.from}`,
136
- blockNumber: block.number.toString(),
137
- });
138
- }
139
- }
140
- }
141
- }
142
- // Sort descending by block number
143
- history.sort((a, b) => Number(BigInt(b.blockNumber) - BigInt(a.blockNumber)));
144
- }
145
- catch (err) {
146
- return fail("HISTORY_FETCH_FAILED", `Failed to fetch history: ${err.message}`, true, "get_execution_history");
147
- }
148
- return ok({
149
- walletAddress: input.walletAddress,
150
- totalFetched: history.length,
151
- history,
152
- note: "ERC-20 history covers last 10,000 blocks via eth_getLogs. Native PHRS transfers cover last 100 blocks via parallel block scan.",
153
- });
154
- }
1
+ // ─── Tool: get_execution_history ───────────────────────────────────────
2
+ // Uses eth_getLogs for ERC-20 Transfer events (fast, covers token swaps)
3
+ // and a parallelised block scan of the last 100 blocks for native PHRS transfers.
4
+ // Prior approach scanned 500 blocks sequentially — 500+ RPC calls, always timed out.
5
+ // ────────────────────────────────────────────────────────────────────────
6
+ import { z } from "zod";
7
+ import { publicClient, getExplorerUrl } from "../lib/pharosClient.js";
8
+ import { DODO_ROUTE_PROXY_ADDRESS, USDC_ADDRESS, USDT_ADDRESS, WBTC_ADDRESS, WETH_ADDRESS, WPHRS_ADDRESS, CIRCLE_USDC_ADDRESS, } from "../lib/constants.js";
9
+ import { formatEther, parseAbiItem } from "viem";
10
+ import { ok, fail } from "../lib/toolResponse.js";
11
+ const TRANSFER_EVENT = parseAbiItem("event Transfer(address indexed from, address indexed to, uint256 value)");
12
+ export const getExecutionHistorySchema = z.object({
13
+ walletAddress: z.string(),
14
+ limit: z.number().optional().default(20),
15
+ filter: z.enum(["swap", "transfer", "all"]).optional().default("all"),
16
+ });
17
+ // All known token addresses to scan for Transfer events (mutable array for viem compatibility)
18
+ const KNOWN_TOKEN_ADDRESSES = [
19
+ USDC_ADDRESS,
20
+ USDT_ADDRESS,
21
+ WBTC_ADDRESS,
22
+ WETH_ADDRESS,
23
+ WPHRS_ADDRESS,
24
+ CIRCLE_USDC_ADDRESS,
25
+ ];
26
+ const ERC20_LOG_RANGE = 10000n; // blocks to scan for ERC-20 events
27
+ const NATIVE_SCAN_RANGE = 100n; // blocks for native PHRS scan
28
+ const NATIVE_BATCH_SIZE = 10; // parallel block fetches per batch
29
+ export const getExecutionHistoryTool = {
30
+ name: "get_execution_history",
31
+ description: "Pull on-chain transaction history for a wallet. Uses eth_getLogs for ERC-20 transfers and parallel block scan for native PHRS. Filters by swap, transfer, or all.",
32
+ inputSchema: getExecutionHistorySchema,
33
+ };
34
+ export async function handleGetExecutionHistory(input) {
35
+ const seen = new Set();
36
+ const history = [];
37
+ try {
38
+ const blockNumber = await publicClient.getBlockNumber();
39
+ const erc20FromBlock = blockNumber > ERC20_LOG_RANGE ? blockNumber - ERC20_LOG_RANGE : 0n;
40
+ const nativeFromBlock = blockNumber > NATIVE_SCAN_RANGE ? blockNumber - NATIVE_SCAN_RANGE : 0n;
41
+ const walletAddr = input.walletAddress;
42
+ // ── ERC-20 Transfer logs ─────────────────────────────────────────────
43
+ // Two queries: wallet as sender (from) and wallet as receiver (to)
44
+ const empty = [];
45
+ const [sentLogs, receivedLogs] = await Promise.all([
46
+ publicClient.getLogs({
47
+ fromBlock: erc20FromBlock,
48
+ toBlock: blockNumber,
49
+ address: KNOWN_TOKEN_ADDRESSES,
50
+ event: TRANSFER_EVENT,
51
+ args: { from: walletAddr },
52
+ strict: true,
53
+ }).catch(() => empty),
54
+ publicClient.getLogs({
55
+ fromBlock: erc20FromBlock,
56
+ toBlock: blockNumber,
57
+ address: KNOWN_TOKEN_ADDRESSES,
58
+ event: TRANSFER_EVENT,
59
+ args: { to: walletAddr },
60
+ strict: true,
61
+ }).catch(() => empty),
62
+ ]);
63
+ const allLogs = [...sentLogs, ...receivedLogs];
64
+ // Resolve block timestamps in bulk for unique block numbers in logs
65
+ const uniqueBlocks = [...new Set(allLogs.map((l) => l.blockNumber))];
66
+ const blockTimestampMap = new Map();
67
+ for (let i = 0; i < uniqueBlocks.length; i += NATIVE_BATCH_SIZE) {
68
+ const batch = uniqueBlocks.slice(i, i + NATIVE_BATCH_SIZE);
69
+ const blocks = await Promise.all(batch.map((bn) => publicClient.getBlock({ blockNumber: bn }).catch(() => null)));
70
+ for (const block of blocks) {
71
+ if (block) {
72
+ blockTimestampMap.set(block.number, new Date(Number(block.timestamp) * 1000).toISOString());
73
+ }
74
+ }
75
+ }
76
+ for (const log of allLogs) {
77
+ if (history.length >= input.limit)
78
+ break;
79
+ if (!log.transactionHash || seen.has(log.transactionHash))
80
+ continue;
81
+ const isDodoSwap = log.address.toLowerCase() === DODO_ROUTE_PROXY_ADDRESS.toLowerCase()
82
+ || sentLogs.some((l) => l.transactionHash === log.transactionHash && l.address.toLowerCase() === DODO_ROUTE_PROXY_ADDRESS.toLowerCase())
83
+ || receivedLogs.some((l) => l.transactionHash === log.transactionHash && l.address.toLowerCase() === DODO_ROUTE_PROXY_ADDRESS.toLowerCase());
84
+ const txType = isDodoSwap ? "swap" : "transfer";
85
+ if (input.filter !== "all" && txType !== input.filter)
86
+ continue;
87
+ seen.add(log.transactionHash);
88
+ history.push({
89
+ txHash: log.transactionHash,
90
+ explorerUrl: getExplorerUrl(log.transactionHash),
91
+ type: txType,
92
+ timestamp: blockTimestampMap.get(log.blockNumber) ?? "unknown",
93
+ status: "unknown", // receipt not fetched per-log to avoid N+1
94
+ value: "0",
95
+ details: `ERC-20 Transfer on block ${log.blockNumber.toString()}`,
96
+ blockNumber: log.blockNumber.toString(),
97
+ });
98
+ }
99
+ // ── Native PHRS transfers — parallel block scan (last 100 blocks) ───
100
+ if (input.filter !== "swap" && history.length < input.limit) {
101
+ const blockNums = [];
102
+ for (let b = blockNumber; b > nativeFromBlock; b--) {
103
+ blockNums.push(b);
104
+ }
105
+ for (let i = 0; i < blockNums.length && history.length < input.limit; i += NATIVE_BATCH_SIZE) {
106
+ const batch = blockNums.slice(i, i + NATIVE_BATCH_SIZE);
107
+ const blocks = await Promise.all(batch.map((bn) => publicClient.getBlock({ blockNumber: bn, includeTransactions: true }).catch(() => null)));
108
+ for (const block of blocks) {
109
+ if (!block)
110
+ continue;
111
+ const ts = new Date(Number(block.timestamp) * 1000).toISOString();
112
+ for (const tx of block.transactions) {
113
+ if (history.length >= input.limit)
114
+ break;
115
+ if (typeof tx === "string")
116
+ continue;
117
+ if (tx.value === 0n)
118
+ continue; // no native transfer
119
+ const isFrom = tx.from.toLowerCase() === input.walletAddress.toLowerCase();
120
+ const isTo = tx.to?.toLowerCase() === input.walletAddress.toLowerCase();
121
+ if (!isFrom && !isTo)
122
+ continue;
123
+ if (seen.has(tx.hash))
124
+ continue;
125
+ seen.add(tx.hash);
126
+ history.push({
127
+ txHash: tx.hash,
128
+ explorerUrl: getExplorerUrl(tx.hash),
129
+ type: "transfer",
130
+ timestamp: ts,
131
+ status: "unknown",
132
+ value: formatEther(tx.value),
133
+ details: isFrom
134
+ ? `Sent ${formatEther(tx.value)} PHRS to ${tx.to}`
135
+ : `Received ${formatEther(tx.value)} PHRS from ${tx.from}`,
136
+ blockNumber: block.number.toString(),
137
+ });
138
+ }
139
+ }
140
+ }
141
+ }
142
+ // Sort descending by block number
143
+ history.sort((a, b) => Number(BigInt(b.blockNumber) - BigInt(a.blockNumber)));
144
+ }
145
+ catch (err) {
146
+ return fail("HISTORY_FETCH_FAILED", `Failed to fetch history: ${err.message}`, true, "get_execution_history");
147
+ }
148
+ return ok({
149
+ walletAddress: input.walletAddress,
150
+ totalFetched: history.length,
151
+ history,
152
+ note: "ERC-20 history covers last 10,000 blocks via eth_getLogs. Native PHRS transfers cover last 100 blocks via parallel block scan.",
153
+ });
154
+ }
155
155
  //# sourceMappingURL=getExecutionHistory.js.map