@conquest-eth/tools 0.0.0 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (178) hide show
  1. package/README.md +20 -51
  2. package/dist/cli-tool-generator.d.ts +2 -1
  3. package/dist/cli-tool-generator.d.ts.map +1 -1
  4. package/dist/cli.js +61 -32
  5. package/dist/cli.js.map +1 -1
  6. package/dist/contracts/space-info.d.ts.map +1 -1
  7. package/dist/contracts/space-info.js +22 -1
  8. package/dist/contracts/space-info.js.map +1 -1
  9. package/dist/fleet/resolve.d.ts +1 -1
  10. package/dist/fleet/resolve.d.ts.map +1 -1
  11. package/dist/fleet/resolve.js +5 -4
  12. package/dist/fleet/resolve.js.map +1 -1
  13. package/dist/fleet/send.d.ts.map +1 -1
  14. package/dist/fleet/send.js +8 -8
  15. package/dist/fleet/send.js.map +1 -1
  16. package/dist/index.d.ts +6 -32
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/index.js +21 -128
  19. package/dist/index.js.map +1 -1
  20. package/dist/mcp.d.ts +14 -0
  21. package/dist/mcp.d.ts.map +1 -0
  22. package/dist/mcp.js +29 -0
  23. package/dist/mcp.js.map +1 -0
  24. package/dist/planet/acquire.d.ts +3 -2
  25. package/dist/planet/acquire.d.ts.map +1 -1
  26. package/dist/planet/acquire.js +6 -4
  27. package/dist/planet/acquire.js.map +1 -1
  28. package/dist/planet/exit.d.ts.map +1 -1
  29. package/dist/planet/exit.js +1 -0
  30. package/dist/planet/exit.js.map +1 -1
  31. package/dist/planet/manager.d.ts +63 -0
  32. package/dist/planet/manager.d.ts.map +1 -1
  33. package/dist/planet/manager.js +125 -2
  34. package/dist/planet/manager.js.map +1 -1
  35. package/dist/planet/withdraw.d.ts +17 -0
  36. package/dist/planet/withdraw.d.ts.map +1 -0
  37. package/dist/planet/withdraw.js +25 -0
  38. package/dist/planet/withdraw.js.map +1 -0
  39. package/dist/storage/interface.d.ts +6 -0
  40. package/dist/storage/interface.d.ts.map +1 -1
  41. package/dist/storage/json-storage.d.ts +1 -0
  42. package/dist/storage/json-storage.d.ts.map +1 -1
  43. package/dist/storage/json-storage.js +10 -1
  44. package/dist/storage/json-storage.js.map +1 -1
  45. package/dist/tool-handling/cli-tool-generator.d.ts +22 -0
  46. package/dist/tool-handling/cli-tool-generator.d.ts.map +1 -0
  47. package/dist/tool-handling/cli-tool-generator.js +345 -0
  48. package/dist/tool-handling/cli-tool-generator.js.map +1 -0
  49. package/dist/tool-handling/cli.d.ts +19 -0
  50. package/dist/tool-handling/cli.d.ts.map +1 -0
  51. package/dist/tool-handling/cli.js +472 -0
  52. package/dist/tool-handling/cli.js.map +1 -0
  53. package/dist/tool-handling/index.d.ts +15 -0
  54. package/dist/tool-handling/index.d.ts.map +1 -0
  55. package/dist/tool-handling/index.js +32 -0
  56. package/dist/tool-handling/index.js.map +1 -0
  57. package/dist/tool-handling/mcp.d.ts +22 -0
  58. package/dist/tool-handling/mcp.d.ts.map +1 -0
  59. package/dist/tool-handling/mcp.js +88 -0
  60. package/dist/tool-handling/mcp.js.map +1 -0
  61. package/dist/tool-handling/types.d.ts +72 -0
  62. package/dist/tool-handling/types.d.ts.map +1 -0
  63. package/dist/tool-handling/types.js +10 -0
  64. package/dist/tool-handling/types.js.map +1 -0
  65. package/dist/tools/acquire_planets.d.ts +7 -5
  66. package/dist/tools/acquire_planets.d.ts.map +1 -1
  67. package/dist/tools/acquire_planets.js +28 -42
  68. package/dist/tools/acquire_planets.js.map +1 -1
  69. package/dist/tools/exit_planets.d.ts +7 -3
  70. package/dist/tools/exit_planets.d.ts.map +1 -1
  71. package/dist/tools/exit_planets.js +20 -9
  72. package/dist/tools/exit_planets.js.map +1 -1
  73. package/dist/tools/get_my_planets.d.ts +3 -2
  74. package/dist/tools/get_my_planets.d.ts.map +1 -1
  75. package/dist/tools/get_my_planets.js +5 -4
  76. package/dist/tools/get_my_planets.js.map +1 -1
  77. package/dist/tools/get_native_token_balance.d.ts +6 -0
  78. package/dist/tools/get_native_token_balance.d.ts.map +1 -0
  79. package/dist/tools/get_native_token_balance.js +64 -0
  80. package/dist/tools/get_native_token_balance.js.map +1 -0
  81. package/dist/tools/get_pending_exits.d.ts +2 -1
  82. package/dist/tools/get_pending_exits.d.ts.map +1 -1
  83. package/dist/tools/get_pending_exits.js +5 -4
  84. package/dist/tools/get_pending_exits.js.map +1 -1
  85. package/dist/tools/get_pending_fleets.d.ts +2 -1
  86. package/dist/tools/get_pending_fleets.d.ts.map +1 -1
  87. package/dist/tools/get_pending_fleets.js +5 -4
  88. package/dist/tools/get_pending_fleets.js.map +1 -1
  89. package/dist/tools/get_planets_around.d.ts +8 -4
  90. package/dist/tools/get_planets_around.d.ts.map +1 -1
  91. package/dist/tools/get_planets_around.js +40 -15
  92. package/dist/tools/get_planets_around.js.map +1 -1
  93. package/dist/tools/get_play_token_balance.d.ts +6 -0
  94. package/dist/tools/get_play_token_balance.d.ts.map +1 -0
  95. package/dist/tools/get_play_token_balance.js +80 -0
  96. package/dist/tools/get_play_token_balance.js.map +1 -0
  97. package/dist/tools/index.d.ts +7 -1
  98. package/dist/tools/index.d.ts.map +1 -1
  99. package/dist/tools/index.js +7 -1
  100. package/dist/tools/index.js.map +1 -1
  101. package/dist/tools/missiv_get_user.d.ts +6 -0
  102. package/dist/tools/missiv_get_user.d.ts.map +1 -0
  103. package/dist/tools/missiv_get_user.js +50 -0
  104. package/dist/tools/missiv_get_user.js.map +1 -0
  105. package/dist/tools/missiv_register.d.ts +6 -0
  106. package/dist/tools/missiv_register.d.ts.map +1 -0
  107. package/dist/tools/missiv_register.js +103 -0
  108. package/dist/tools/missiv_register.js.map +1 -0
  109. package/dist/tools/resolve_fleet.d.ts +3 -2
  110. package/dist/tools/resolve_fleet.d.ts.map +1 -1
  111. package/dist/tools/resolve_fleet.js +5 -4
  112. package/dist/tools/resolve_fleet.js.map +1 -1
  113. package/dist/tools/send_fleet.d.ts +3 -2
  114. package/dist/tools/send_fleet.d.ts.map +1 -1
  115. package/dist/tools/send_fleet.js +16 -15
  116. package/dist/tools/send_fleet.js.map +1 -1
  117. package/dist/tools/simulate.d.ts +14 -0
  118. package/dist/tools/simulate.d.ts.map +1 -0
  119. package/dist/tools/simulate.js +123 -0
  120. package/dist/tools/simulate.js.map +1 -0
  121. package/dist/tools/simulate_multiple.d.ts +17 -0
  122. package/dist/tools/simulate_multiple.d.ts.map +1 -0
  123. package/dist/tools/simulate_multiple.js +166 -0
  124. package/dist/tools/simulate_multiple.js.map +1 -0
  125. package/dist/tools/verify_exit_status.d.ts +5 -3
  126. package/dist/tools/verify_exit_status.d.ts.map +1 -1
  127. package/dist/tools/verify_exit_status.js +12 -8
  128. package/dist/tools/verify_exit_status.js.map +1 -1
  129. package/dist/tools/withdraw.d.ts +9 -0
  130. package/dist/tools/withdraw.d.ts.map +1 -0
  131. package/dist/tools/withdraw.js +86 -0
  132. package/dist/tools/withdraw.js.map +1 -0
  133. package/dist/types.d.ts +31 -28
  134. package/dist/types.d.ts.map +1 -1
  135. package/dist/types.js +1 -33
  136. package/dist/types.js.map +1 -1
  137. package/dist/util/time.d.ts +0 -30
  138. package/dist/util/time.d.ts.map +1 -1
  139. package/dist/util/time.js +0 -36
  140. package/dist/util/time.js.map +1 -1
  141. package/package.json +80 -77
  142. package/src/cli.ts +88 -59
  143. package/src/contracts/space-info.ts +24 -1
  144. package/src/fleet/resolve.ts +5 -4
  145. package/src/fleet/send.ts +9 -8
  146. package/src/index.ts +28 -162
  147. package/src/mcp.ts +46 -0
  148. package/src/planet/acquire.ts +6 -6
  149. package/src/planet/exit.ts +1 -0
  150. package/src/planet/manager.ts +163 -0
  151. package/src/planet/withdraw.ts +33 -0
  152. package/src/storage/interface.ts +7 -0
  153. package/src/storage/json-storage.ts +11 -1
  154. package/src/tool-handling/cli.ts +559 -0
  155. package/src/tool-handling/index.ts +45 -0
  156. package/src/tool-handling/mcp.ts +127 -0
  157. package/src/tool-handling/types.ts +86 -0
  158. package/src/tools/acquire_planets.ts +34 -60
  159. package/src/tools/exit_planets.ts +25 -12
  160. package/src/tools/get_native_token_balance.ts +72 -0
  161. package/src/tools/get_pending_exits.ts +8 -5
  162. package/src/tools/get_pending_fleets.ts +8 -5
  163. package/src/tools/get_planets_around.ts +45 -16
  164. package/src/tools/get_play_token_balance.ts +90 -0
  165. package/src/tools/index.ts +7 -1
  166. package/src/tools/missiv_get_user.ts +68 -0
  167. package/src/tools/missiv_register.ts +122 -0
  168. package/src/tools/resolve_fleet.ts +8 -5
  169. package/src/tools/send_fleet.ts +21 -18
  170. package/src/tools/simulate.ts +141 -0
  171. package/src/tools/simulate_multiple.ts +197 -0
  172. package/src/tools/verify_exit_status.ts +15 -11
  173. package/src/tools/withdraw.ts +100 -0
  174. package/src/types.ts +33 -71
  175. package/src/util/time.ts +0 -46
  176. package/src/cli-tool-generator.ts +0 -287
  177. package/src/helpers/index.ts +0 -59
  178. package/src/tools/get_my_planets.ts +0 -30
package/package.json CHANGED
@@ -1,78 +1,81 @@
1
1
  {
2
- "name": "@conquest-eth/tools",
3
- "version": "0.0.0",
4
- "description": "Tools for Conquest.eth, including mcp server",
5
- "author": "Ronan Sandford",
6
- "license": "MIT",
7
- "keywords": [
8
- "mcp",
9
- "ethereum",
10
- "tools",
11
- "cli",
12
- "conquest.eth"
13
- ],
14
- "repository": {
15
- "type": "git",
16
- "url": "git+https://github.com/wighawag/tools-conquest-eth-v0.git"
17
- },
18
- "homepage": "https://github.com/wighawag/tools-conquest-eth-v0#readme",
19
- "bugs": {
20
- "url": "https://github.com/wighawag/tools-conquest-eth-v0/issues"
21
- },
22
- "publishConfig": {
23
- "access": "public"
24
- },
25
- "type": "module",
26
- "main": "./dist/index.js",
27
- "module": "./dist/index.js",
28
- "types": "./dist/index.d.ts",
29
- "exports": {
30
- ".": {
31
- "types": "./dist/index.d.ts",
32
- "import": "./dist/index.js"
33
- }
34
- },
35
- "bin": {
36
- "conquest": "dist/cli.js"
37
- },
38
- "files": [
39
- "dist",
40
- "src"
41
- ],
42
- "sideEffects": false,
43
- "dependencies": {
44
- "@modelcontextprotocol/sdk": "^1.25.3",
45
- "commander": "^14.0.3",
46
- "conquest-eth-v0-contracts": "workspace:*",
47
- "eip-1193": "^0.6.5",
48
- "ldenv": "^0.3.16",
49
- "remote-procedure-call": "^0.1.1",
50
- "tools-ethereum": "^0.0.1",
51
- "viem": "^2.45.1",
52
- "zod": "^4.3.6"
53
- },
54
- "devDependencies": {
55
- "@changesets/cli": "^2.29.8",
56
- "@modelcontextprotocol/inspector": "^0.19.0",
57
- "@types/node": "^25.2.0",
58
- "as-soon": "^0.1.5",
59
- "prettier": "^3.8.0",
60
- "reloaderoo": "^1.1.5",
61
- "tsx": "^4.21.0",
62
- "typescript": "^5.3.3",
63
- "vitest": "^4.0.18"
64
- },
65
- "scripts": {
66
- "prepublishOnly": "pnpm format:check && pnpm build",
67
- "release": "pnpm prepublishOnly && git push --all && pnpm changeset publish && git push --tags",
68
- "format": "prettier --write .",
69
- "format:check": "prettier --check .",
70
- "build": "tsc",
71
- "dev": "as-soon -w src pnpm build",
72
- "test": "vitest run",
73
- "test:watch": "vitest",
74
- "mcp:inspector": "mcp-inspector",
75
- "reloaderoo": "reloaderoo",
76
- "start": "reloaderoo proxy --log-level debug -- node ./dist/cli.js --rpc-url http://localhost:8545 --game-contract 0x322813fd9a801c5507c9de605d63cea4f2ce6c44 mcp --ethereum"
77
- }
78
- }
2
+ "name": "@conquest-eth/tools",
3
+ "version": "0.0.2",
4
+ "description": "Tools for Conquest.eth, including mcp server",
5
+ "author": "Ronan Sandford",
6
+ "license": "MIT",
7
+ "keywords": [
8
+ "mcp",
9
+ "ethereum",
10
+ "tools",
11
+ "cli",
12
+ "conquest.eth"
13
+ ],
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "git+https://github.com/wighawag/tools-conquest-eth-v0.git"
17
+ },
18
+ "homepage": "https://github.com/wighawag/tools-conquest-eth-v0#readme",
19
+ "bugs": {
20
+ "url": "https://github.com/wighawag/tools-conquest-eth-v0/issues"
21
+ },
22
+ "publishConfig": {
23
+ "access": "public"
24
+ },
25
+ "type": "module",
26
+ "main": "./dist/index.js",
27
+ "module": "./dist/index.js",
28
+ "types": "./dist/index.d.ts",
29
+ "exports": {
30
+ ".": {
31
+ "types": "./dist/index.d.ts",
32
+ "import": "./dist/index.js"
33
+ }
34
+ },
35
+ "bin": {
36
+ "conquest": "dist/cli.js"
37
+ },
38
+ "files": [
39
+ "dist",
40
+ "src"
41
+ ],
42
+ "sideEffects": false,
43
+ "dependencies": {
44
+ "@ethersproject/signing-key": "5.7.0",
45
+ "@modelcontextprotocol/sdk": "^1.25.3",
46
+ "@noble/hashes": "^1.6.1",
47
+ "@noble/secp256k1": "^2.1.0",
48
+ "commander": "^14.0.3",
49
+ "eip-1193": "^0.6.5",
50
+ "ldenv": "^0.3.16",
51
+ "remote-procedure-call": "^0.1.1",
52
+ "tools-ethereum": "^0.0.7",
53
+ "viem": "^2.45.1",
54
+ "zod": "^4.3.6",
55
+ "conquest-eth-v0-contracts": "0.0.1"
56
+ },
57
+ "devDependencies": {
58
+ "@modelcontextprotocol/inspector": "^0.19.0",
59
+ "@rocketh/node": "^0.18.2",
60
+ "@types/node": "^25.2.0",
61
+ "as-soon": "^0.1.5",
62
+ "prettier": "^3.8.0",
63
+ "prool": "^0.2.2",
64
+ "reloaderoo": "^1.1.5",
65
+ "tsx": "^4.21.0",
66
+ "typescript": "^5.3.3",
67
+ "vitest": "^4.0.18"
68
+ },
69
+ "scripts": {
70
+ "release": "pnpm prepublishOnly && git push --all && pnpm changeset publish && git push --tags",
71
+ "format": "prettier --write .",
72
+ "format:check": "prettier --check .",
73
+ "build": "tsc",
74
+ "dev": "as-soon -w src pnpm build",
75
+ "test": "vitest run",
76
+ "test:watch": "vitest",
77
+ "mcp:inspector": "mcp-inspector",
78
+ "reloaderoo": "reloaderoo",
79
+ "start": "reloaderoo proxy --log-level debug -- node ./dist/cli.js --rpc-url http://localhost:8545 --game-contract 0x322813fd9a801c5507c9de605d63cea4f2ce6c44 mcp --ethereum"
80
+ }
81
+ }
package/src/cli.ts CHANGED
@@ -1,12 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
  import {StdioServerTransport} from '@modelcontextprotocol/sdk/server/stdio.js';
3
- import {createServer} from './index.js';
4
3
  import {Command} from 'commander';
5
4
  import pkg from '../package.json' with {type: 'json'};
6
- import {getChain} from 'tools-ethereum/helpers';
7
5
  import {loadEnv} from 'ldenv';
8
6
  import * as tools from './tools/index.js';
9
- import {registerAllToolCommands} from './cli-tool-generator.js';
7
+ import {type EnvFactory, registerAllToolCommands} from './tool-handling/cli.js';
8
+ import type {ConquestEnv} from './types.js';
9
+ import {createServer} from './mcp.js';
10
+ import {createConquestEnv} from './index.js';
10
11
 
11
12
  loadEnv();
12
13
 
@@ -15,6 +16,9 @@ const program = new Command();
15
16
  // Get the binary name from package.json
16
17
  const binName = Object.keys(pkg.bin || {})[0];
17
18
 
19
+ // ----------------------------------------------------------------------------
20
+ // GLOBAL OPTIONS
21
+ // ----------------------------------------------------------------------------
18
22
  program
19
23
  .name(binName)
20
24
  .description(pkg.description || 'Conquest.eth CLI - MCP server and direct tool execution')
@@ -32,16 +36,65 @@ program
32
36
  'Path to storage directory',
33
37
  process.env.STORAGE_PATH || './data',
34
38
  )
35
- .option(
36
- '--private-key <key>',
37
- 'Private key for sending transactions',
38
- process.env.PRIVATE_KEY || '',
39
- )
40
39
  .action(() => {
41
40
  program.help();
42
41
  });
43
42
 
43
+ function gatherGlobalOptions(program: Command) {
44
+ const globalOptions = program.opts();
45
+ const rpcUrl = globalOptions.rpcUrl;
46
+ const gameContract = globalOptions.gameContract;
47
+
48
+ const storage = globalOptions.storage;
49
+ const storagePath = globalOptions.storagePath;
50
+
51
+ // Validate required options
52
+ if (!rpcUrl) {
53
+ console.error('Error: --rpc-url option or RPC_URL environment variable is required');
54
+ process.exit(1);
55
+ }
56
+
57
+ if (!gameContract) {
58
+ console.error(
59
+ 'Error: --game-contract option or GAME_CONTRACT environment variable is required',
60
+ );
61
+ process.exit(1);
62
+ }
63
+
64
+ const privateKey = process.env.PRIVATE_KEY as `0x${string}`;
65
+ // Warn if private key is not provided for write operations
66
+ if (!privateKey) {
67
+ console.warn('Warning: PRIVATE_KEY environment variable is required for sending transactions');
68
+ } else if (!privateKey.startsWith('0x')) {
69
+ console.error('Error: PRIVATE_KEY must start with 0x');
70
+ process.exit(1);
71
+ }
72
+
73
+ return {rpcUrl, gameContract, privateKey, storage, storagePath};
74
+ }
75
+ // ----------------------------------------------------------------------------
76
+
77
+ // ----------------------------------------------------------------------------
78
+ // FACTORY THAT GENERATE THE Conquest Environment Used By Tools
79
+ // ----------------------------------------------------------------------------
80
+ /**
81
+ * Factory function to create the ConquestEnv from CLI options
82
+ */
83
+ const envFactory: EnvFactory<ConquestEnv> = async () => {
84
+ const {rpcUrl, gameContract, privateKey, storage, storagePath} = gatherGlobalOptions(program);
85
+
86
+ return createConquestEnv({
87
+ rpcUrl,
88
+ gameContract,
89
+ privateKey,
90
+ storagePath,
91
+ });
92
+ };
93
+ // ----------------------------------------------------------------------------
94
+
95
+ // ----------------------------------------------------------------------------
44
96
  // MCP subcommand - starts the MCP server
97
+ // ----------------------------------------------------------------------------
45
98
  program
46
99
  .command('mcp')
47
100
  .description('Start the MCP server')
@@ -50,60 +103,36 @@ program
50
103
  'Whether to also provide tools-ethereum tools',
51
104
  process.env.ETHEREUM_TOOLS === 'true',
52
105
  )
53
- .action(async () => {
54
- const options = program.opts();
55
- const mcpOptions = program.commands.find((cmd) => cmd.name() === 'mcp')?.opts() || {};
56
-
57
- const rpcUrl = options.rpcUrl;
58
- const gameContract = options.gameContract;
59
- const ethereum = mcpOptions.ethereum ?? process.env.ETHEREUM_TOOLS === 'true';
60
- const privateKey = options.privateKey;
61
- const storage = options.storage;
62
- const storagePath = options.storagePath;
63
-
64
- // Validate required options
65
- if (!rpcUrl) {
66
- console.error('Error: --rpc-url option or RPC_URL environment variable is required');
67
- process.exit(1);
68
- }
69
-
70
- if (!gameContract) {
71
- console.error(
72
- 'Error: --game-contract option or GAME_CONTRACT environment variable is required',
73
- );
74
- process.exit(1);
75
- }
76
-
77
- // Warn if private key is not provided for write operations
78
- if (!privateKey) {
79
- console.warn(
80
- 'Warning: PRIVATE_KEY environment variable is required for sending transactions',
81
- );
82
- } else if (!privateKey.startsWith('0x')) {
83
- console.error('Error: PRIVATE_KEY must start with 0x');
84
- process.exit(1);
85
- }
86
-
87
- const chain = await getChain(rpcUrl);
106
+ .action(async (options) => {
107
+ const env = await envFactory();
108
+
109
+ const ethereum = options.ethereum ?? process.env.ETHEREUM_TOOLS === 'true';
110
+
88
111
  const transport = new StdioServerTransport();
89
- const server = createServer(
90
- {
91
- chain,
92
- privateKey: privateKey as `0x${string}`,
93
- gameContract: gameContract as `0x${string}`,
94
- },
95
- {
96
- ethereum,
97
- storageConfig: {
98
- type: storage as 'json' | 'sqlite',
99
- dataDir: storagePath,
100
- },
101
- },
102
- );
112
+ const server = await createServer(env, {ethereum});
103
113
  await server.connect(transport);
104
114
  });
115
+ // ----------------------------------------------------------------------------
116
+
117
+ // ----------------------------------------------------------------------------
118
+ // Register all tool commands dynamically with the CLI config
119
+ registerAllToolCommands(program, tools, envFactory);
120
+ // ----------------------------------------------------------------------------
121
+
122
+ // ----------------------------------------------------------------------------
123
+ // HANDLE unknown command
124
+ // ----------------------------------------------------------------------------
125
+ const args = process.argv.slice(2);
126
+ const registeredCommands = program.commands.map((cmd) => cmd.name());
127
+
128
+ // Check if the first argument is a known command or a global flag
129
+ const isKnown = registeredCommands.includes(args[0]) || args[0]?.startsWith('-');
105
130
 
106
- // Register all tool commands dynamically
107
- registerAllToolCommands(program, tools);
131
+ if (args.length > 0 && !isKnown) {
132
+ console.error(`error: unknown command: ${args[0]}`);
133
+ program.outputHelp();
134
+ process.exit(1);
135
+ }
136
+ // ----------------------------------------------------------------------------
108
137
 
109
138
  program.parse(process.argv);
@@ -1,6 +1,17 @@
1
1
  import {SpaceInfo} from 'conquest-eth-v0-contracts';
2
2
  import type {ClientsWithOptionalWallet, ContractConfig, GameContract} from '../types.js';
3
3
 
4
+ // Minimal ABI for reading numTokensPerNativeTokenAt18Decimals from PlayToken
5
+ const PlayTokenAbi = [
6
+ {
7
+ name: 'numTokensPerNativeTokenAt18Decimals',
8
+ type: 'function',
9
+ stateMutability: 'view',
10
+ inputs: [],
11
+ outputs: [{type: 'uint256'}],
12
+ },
13
+ ] as const;
14
+
4
15
  export async function createSpaceInfo(
5
16
  clients: ClientsWithOptionalWallet,
6
17
  gameContract: GameContract,
@@ -11,19 +22,31 @@ export async function createSpaceInfo(
11
22
  functionName: 'getConfig',
12
23
  });
13
24
 
25
+ // Extract stakingToken address and fetch numTokensPerNativeToken from PlayToken contract
26
+ const stakingToken = config.stakingToken as `0x${string}`;
27
+ const numTokensPerNativeToken = await clients.publicClient.readContract({
28
+ address: stakingToken,
29
+ abi: PlayTokenAbi,
30
+ functionName: 'numTokensPerNativeTokenAt18Decimals',
31
+ });
32
+
14
33
  const contractConfig: ContractConfig = {
15
34
  genesis: BigInt(config.genesis),
16
35
  resolveWindow: BigInt(config.resolveWindow),
17
36
  timePerDistance: BigInt(config.timePerDistance),
18
37
  exitDuration: BigInt(config.exitDuration),
19
38
  acquireNumSpaceships: Number(config.acquireNumSpaceships),
39
+ stakingToken,
40
+ numTokensPerNativeToken: BigInt(numTokensPerNativeToken),
20
41
  };
21
42
 
22
43
  // Create SpaceInfo instance with config
44
+ // Note: The contract's getConfig() returns timePerDistance already divided by 4 (see OuterSpaceFacetBase constructor).
45
+ // SpaceInfo also divides by 4 internally, so we need to multiply by 4 here to compensate.
23
46
  const spaceInfo = new SpaceInfo({
24
47
  genesis: config.genesis as `0x${string}`,
25
48
  resolveWindow: Number(config.resolveWindow),
26
- timePerDistance: Number(config.timePerDistance),
49
+ timePerDistance: Number(config.timePerDistance) * 4,
27
50
  exitDuration: Number(config.exitDuration),
28
51
  acquireNumSpaceships: Number(config.acquireNumSpaceships),
29
52
  productionSpeedUp: Number(config.productionSpeedUp),
@@ -166,7 +166,7 @@ export async function resolveFleetWithSpaceInfo(
166
166
  }
167
167
 
168
168
  /**
169
- * Get fleets that can be resolved (not yet resolved and past resolve window)
169
+ * Get fleets that can be resolved (arrived and still within resolve window)
170
170
  *
171
171
  * @param storage - Storage instance for tracking pending fleets
172
172
  * @param resolveWindow - The resolve window duration in seconds from contract config
@@ -180,8 +180,9 @@ export async function getResolvableFleets(
180
180
  const fleets = await storage.getResolvableFleets();
181
181
 
182
182
  return fleets.filter((fleet) => {
183
- // Check if fleet is past the resolve window
184
- const resolveWindowOpen = fleet.estimatedArrivalTime + Number(resolveWindow);
185
- return !fleet.resolved && currentTime >= resolveWindowOpen;
183
+ // Fleet can be resolved after arrival but before resolve window closes
184
+ const arrivalTime = fleet.estimatedArrivalTime;
185
+ const resolveWindowEnd = arrivalTime + Number(resolveWindow);
186
+ return !fleet.resolved && currentTime >= arrivalTime && currentTime < resolveWindowEnd;
186
187
  });
187
188
  }
package/src/fleet/send.ts CHANGED
@@ -3,7 +3,6 @@ import type {SpaceInfo} from 'conquest-eth-v0-contracts';
3
3
  import type {FleetStorage} from '../storage/interface.js';
4
4
  import type {Clients, ContractConfig, GameContract, PendingFleet} from '../types.js';
5
5
  import {computeFleetId, computeToHash, generateSecret} from '../util/hashing.js';
6
- import {calculateEstimatedArrivalTime, getCurrentTimestamp} from '../util/time.js';
7
6
 
8
7
  /**
9
8
  * Send a fleet to a destination planet
@@ -56,12 +55,14 @@ export async function sendFleet(
56
55
  // Generate secret if not provided
57
56
  const secret = options?.secret || generateSecret();
58
57
 
59
- // Calculate estimated arrival time using contract config
60
- const estimatedArrivalTime = calculateEstimatedArrivalTime({
61
- startTime: BigInt(getCurrentTimestamp()),
62
- distance: BigInt(distance),
63
- timePerDistance: contractConfig.timePerDistance,
64
- });
58
+ // Get current blockchain timestamp (not local time) for accurate arrival calculation
59
+ const block = await clients.publicClient.getBlock();
60
+ const blockTimestamp = Number(block.timestamp);
61
+
62
+ // Calculate estimated arrival time using SpaceInfo which accounts for planet speed
63
+ // The travel time formula is: distance * (timePerDistance * 10000) / speed
64
+ const travelTime = spaceInfo.timeToArrive(fromPlanet, toPlanet);
65
+ const estimatedArrivalTime = blockTimestamp + travelTime;
65
66
 
66
67
  const parameters = {
67
68
  gift: options?.gift ?? false,
@@ -94,7 +95,7 @@ export async function sendFleet(
94
95
  arrivalTimeWanted: parameters.arrivalTimeWanted,
95
96
  fleetSender,
96
97
  operator,
97
- committedAt: getCurrentTimestamp(),
98
+ committedAt: blockTimestamp,
98
99
  estimatedArrivalTime,
99
100
  resolved: false,
100
101
  };
package/src/index.ts CHANGED
@@ -1,181 +1,47 @@
1
- import {McpServer} from '@modelcontextprotocol/sdk/server/mcp.js';
2
- import pkg from '../package.json' with {type: 'json'};
3
- import {Implementation} from '@modelcontextprotocol/sdk/types.js';
4
- import {Chain} from 'viem';
5
- import {ServerOptions} from '@modelcontextprotocol/sdk/server';
6
- import {createServer as createMCPEthereumServer} from 'tools-ethereum';
7
- import {getClients} from 'tools-ethereum/helpers';
1
+ import {getClients, getChain} from 'tools-ethereum/helpers';
8
2
  import {createSpaceInfo} from './contracts/space-info.js';
9
3
  import {JsonFleetStorage} from './storage/json-storage.js';
10
4
  import {FleetManager} from './fleet/manager.js';
11
5
  import {PlanetManager} from './planet/manager.js';
12
- import type {ClientsWithOptionalWallet, ContractConfig, GameContract} from './types.js';
13
- import {SpaceInfo} from 'conquest-eth-v0-contracts';
6
+ import type {
7
+ ClientsWithOptionalWallet,
8
+ ConquestEnv,
9
+ EnvFactoryOptions,
10
+ GameContract,
11
+ } from './types.js';
14
12
 
15
- // Import refactored tools
16
- import * as tools from './tools/index.js';
17
- import {registerTool, stringifyWithBigInt} from './helpers/index.js';
18
13
  import {Abi_IOuterSpace} from 'conquest-eth-v0-contracts/abis/IOuterSpace.js';
19
14
 
20
15
  /**
21
- * Create and configure an MCP server for Conquest.eth game interactions
16
+ * Factory function to create the ConquestEnv
17
+ * This is shared between CLI and MCP server
22
18
  *
23
- * @param params - Configuration parameters for the server
24
- * @param params.chain - The blockchain chain to connect to
25
- * @param params.privateKey - Optional private key for signing transactions (wallet operations)
26
- * @param params.gameContract - The game contract address
27
- * @param options - Optional server configuration
28
- * @param options.ethereum - Whether to include Ethereum MCP tools (default: false)
29
- * @param options.rpcURL - Optional custom RPC URL
30
- * @param options.serverOptions - Optional MCP server options
31
- * @param options.serverInfo - Optional server metadata to override defaults
32
- * @param options.storageConfig - Storage configuration for fleets and exits
33
- * @param options.storageConfig.type - Storage type ('json' or 'sqlite')
34
- * @param options.storageConfig.dataDir - Optional data directory path
35
- * @returns Configured MCP server instance with Conquest game tools registered
19
+ * @param options - Configuration options for creating the environment
20
+ * @returns ConquestEnv with fleetManager and planetManager
36
21
  */
37
- export function createServer(
38
- params: {chain: Chain; privateKey?: `0x${string}`; gameContract: `0x${string}`},
39
- options?: {
40
- ethereum?: boolean;
41
- rpcURL?: string;
42
- serverOptions?: ServerOptions;
43
- serverInfo?: Implementation;
44
- storageConfig?: {type: 'json' | 'sqlite'; dataDir?: string};
45
- },
46
- ) {
47
- const {gameContract: gameContractAddress, ...mcpEthereumParams} = params;
48
- const clients = getClients(params, options) as ClientsWithOptionalWallet;
22
+ export async function createConquestEnv(options: EnvFactoryOptions): Promise<ConquestEnv> {
23
+ const {rpcUrl, gameContract: gameContractAddress, privateKey, storagePath = './data'} = options;
24
+
25
+ const chain = await getChain(rpcUrl);
26
+ const clients = getClients({
27
+ chain,
28
+ privateKey,
29
+ }) as ClientsWithOptionalWallet;
49
30
 
50
31
  const gameContract: GameContract = {
51
32
  address: gameContractAddress,
52
33
  abi: Abi_IOuterSpace,
53
34
  };
54
35
 
55
- const name = `mcp-conquest-eth-v0`;
56
- const server = options?.ethereum
57
- ? createMCPEthereumServer(mcpEthereumParams, {
58
- ...options,
59
- serverInfo: {name, version: pkg.version, ...options?.serverInfo},
60
- })
61
- : new McpServer(
62
- options?.serverInfo || {
63
- name,
64
- version: pkg.version,
65
- },
66
- options?.serverOptions || {capabilities: {logging: {}}},
67
- );
68
-
69
- // Initialize SpaceInfo and contractConfig
70
- let spaceInfo: SpaceInfo | null = null;
71
- let contractConfig: ContractConfig | null = null;
36
+ const {spaceInfo, contractConfig} = await createSpaceInfo(clients, gameContract);
37
+ const storage = new JsonFleetStorage(storagePath);
72
38
 
73
- const initSpaceInfo = async () => {
74
- if (!spaceInfo || !contractConfig) {
75
- const result = await createSpaceInfo(clients, gameContract);
76
- spaceInfo = result.spaceInfo;
77
- contractConfig = result.contractConfig;
78
- }
79
- return {spaceInfo, contractConfig};
39
+ return {
40
+ fleetManager: new FleetManager(clients, gameContract, spaceInfo, contractConfig, storage),
41
+ planetManager: new PlanetManager(clients, gameContract, spaceInfo, contractConfig, storage),
42
+ spaceInfo,
43
+ contractConfig,
44
+ clients,
45
+ options,
80
46
  };
81
-
82
- // Initialize storage
83
- const storageConfig = options?.storageConfig || {type: 'json', dataDir: './data'};
84
- const storage = new JsonFleetStorage(storageConfig.dataDir || './data');
85
-
86
- // Initialize managers (will be initialized after spaceInfo is ready)
87
- let fleetManager: FleetManager | null = null;
88
- let planetManager: PlanetManager | null = null;
89
-
90
- // Helper to ensure managers are initialized
91
- const ensureManagersInitialized = async () => {
92
- const {spaceInfo: si, contractConfig: cc} = await initSpaceInfo();
93
-
94
- // Initialize fleetManager even without walletClient for read-only operations
95
- if (!fleetManager && si && cc) {
96
- fleetManager = new FleetManager(clients, gameContract, si, cc, storage);
97
- }
98
-
99
- // Initialize planetManager even without walletClient for read-only operations
100
- if (!planetManager && si && cc) {
101
- planetManager = new PlanetManager(clients, gameContract, si, cc, storage);
102
- }
103
-
104
- if (!fleetManager) {
105
- throw new Error('Fleet manager not initialized');
106
- }
107
- if (!planetManager) {
108
- throw new Error('Planet manager not initialized');
109
- }
110
-
111
- return {fleetManager, planetManager};
112
- };
113
-
114
- // Auto-register all tools
115
- for (const [name, tool] of Object.entries(tools)) {
116
- // Skip the file that's not a tool
117
- if (name === 'default') continue;
118
-
119
- server.registerTool(
120
- name,
121
- {
122
- description: tool.description,
123
- inputSchema: tool.schema,
124
- },
125
- async (args: unknown) => {
126
- try {
127
- const {fleetManager, planetManager} = await ensureManagersInitialized();
128
-
129
- const env = {
130
- sendStatus: async (_message: string) => {
131
- // TODO: Implement progress notifications when sessionId is available
132
- },
133
- fleetManager,
134
- planetManager,
135
- };
136
-
137
- const result = await tool.execute(env, args as any);
138
-
139
- // Convert ToolResult to CallToolResult
140
- if (result.success === false) {
141
- return {
142
- content: [
143
- {
144
- type: 'text' as const,
145
- text: stringifyWithBigInt({
146
- error: result.error,
147
- ...(result.stack ? {stack: result.stack} : {}),
148
- }),
149
- },
150
- ],
151
- isError: true,
152
- };
153
- }
154
-
155
- return {
156
- content: [
157
- {
158
- type: 'text' as const,
159
- text: stringifyWithBigInt(result.result, 2),
160
- },
161
- ],
162
- };
163
- } catch (error) {
164
- return {
165
- content: [
166
- {
167
- type: 'text' as const,
168
- text: stringifyWithBigInt({
169
- error: error instanceof Error ? error.message : String(error),
170
- }),
171
- },
172
- ],
173
- isError: true,
174
- };
175
- }
176
- },
177
- );
178
- }
179
-
180
- return server;
181
47
  }