@aephia/atlas-kit 0.6.0-next.17 → 0.6.0-next.170

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 (926) hide show
  1. package/README.md +2 -1
  2. package/dist/cargo/actions/deposit.d.ts +5 -2
  3. package/dist/cargo/actions/deposit.d.ts.map +1 -1
  4. package/dist/cargo/actions/deposit.js +60 -129
  5. package/dist/cargo/actions/deposit.js.map +1 -1
  6. package/dist/cargo/actions/index.d.ts +12 -6
  7. package/dist/cargo/actions/index.d.ts.map +1 -1
  8. package/dist/cargo/actions/index.js +98 -94
  9. package/dist/cargo/actions/index.js.map +1 -1
  10. package/dist/cargo/actions/wallet-context.d.ts +52 -3
  11. package/dist/cargo/actions/wallet-context.d.ts.map +1 -1
  12. package/dist/cargo/actions/wallet-context.js +118 -7
  13. package/dist/cargo/actions/wallet-context.js.map +1 -1
  14. package/dist/cargo/actions/wallet-token.d.ts +1 -6
  15. package/dist/cargo/actions/wallet-token.d.ts.map +1 -1
  16. package/dist/cargo/actions/wallet-token.js +0 -45
  17. package/dist/cargo/actions/wallet-token.js.map +1 -1
  18. package/dist/cargo/actions/withdrawal.d.ts +1 -1
  19. package/dist/cargo/actions/withdrawal.d.ts.map +1 -1
  20. package/dist/cargo/actions/withdrawal.js +39 -168
  21. package/dist/cargo/actions/withdrawal.js.map +1 -1
  22. package/dist/cargo/index.d.ts.map +1 -1
  23. package/dist/cargo/index.js +2 -10
  24. package/dist/cargo/index.js.map +1 -1
  25. package/dist/claim-stakes/actions/index.d.ts +93 -14
  26. package/dist/claim-stakes/actions/index.d.ts.map +1 -1
  27. package/dist/claim-stakes/actions/index.js +739 -742
  28. package/dist/claim-stakes/actions/index.js.map +1 -1
  29. package/dist/claim-stakes/index.d.ts +22 -6
  30. package/dist/claim-stakes/index.d.ts.map +1 -1
  31. package/dist/claim-stakes/index.js +34 -19
  32. package/dist/claim-stakes/index.js.map +1 -1
  33. package/dist/client/context.d.ts.map +1 -1
  34. package/dist/client/context.js +354 -38
  35. package/dist/client/context.js.map +1 -1
  36. package/dist/client/contracts.d.ts +69 -6
  37. package/dist/client/contracts.d.ts.map +1 -1
  38. package/dist/client/deployments.d.ts +17 -0
  39. package/dist/client/deployments.d.ts.map +1 -1
  40. package/dist/client/deployments.js +17 -0
  41. package/dist/client/deployments.js.map +1 -1
  42. package/dist/client/errors.d.ts +2 -2
  43. package/dist/client/errors.d.ts.map +1 -1
  44. package/dist/client/errors.js.map +1 -1
  45. package/dist/client/index.d.ts +57 -1
  46. package/dist/client/index.d.ts.map +1 -1
  47. package/dist/client/index.js +56 -0
  48. package/dist/client/index.js.map +1 -1
  49. package/dist/client/rpc-subscription-provider.d.ts +37 -0
  50. package/dist/client/rpc-subscription-provider.d.ts.map +1 -0
  51. package/dist/client/rpc-subscription-provider.js +224 -0
  52. package/dist/client/rpc-subscription-provider.js.map +1 -0
  53. package/dist/combat/actions/index.d.ts +30 -12
  54. package/dist/combat/actions/index.d.ts.map +1 -1
  55. package/dist/combat/actions/index.js +360 -302
  56. package/dist/combat/actions/index.js.map +1 -1
  57. package/dist/combat/index.d.ts +25 -8
  58. package/dist/combat/index.d.ts.map +1 -1
  59. package/dist/combat/index.js +21 -18
  60. package/dist/combat/index.js.map +1 -1
  61. package/dist/crafting/actions/fee-rate.d.ts +47 -0
  62. package/dist/crafting/actions/fee-rate.d.ts.map +1 -0
  63. package/dist/crafting/actions/fee-rate.js +58 -0
  64. package/dist/crafting/actions/fee-rate.js.map +1 -0
  65. package/dist/crafting/actions/index.d.ts +29 -18
  66. package/dist/crafting/actions/index.d.ts.map +1 -1
  67. package/dist/crafting/actions/index.js +298 -352
  68. package/dist/crafting/actions/index.js.map +1 -1
  69. package/dist/crafting/estimate.d.ts +101 -0
  70. package/dist/crafting/estimate.d.ts.map +1 -0
  71. package/dist/crafting/estimate.js +108 -0
  72. package/dist/crafting/estimate.js.map +1 -0
  73. package/dist/crafting/index.d.ts +34 -3
  74. package/dist/crafting/index.d.ts.map +1 -1
  75. package/dist/crafting/index.js +50 -28
  76. package/dist/crafting/index.js.map +1 -1
  77. package/dist/factions/actions/index.d.ts +2 -1
  78. package/dist/factions/actions/index.d.ts.map +1 -1
  79. package/dist/factions/actions/index.js +6 -4
  80. package/dist/factions/actions/index.js.map +1 -1
  81. package/dist/factions/index.d.ts +19 -2
  82. package/dist/factions/index.d.ts.map +1 -1
  83. package/dist/factions/index.js +38 -26
  84. package/dist/factions/index.js.map +1 -1
  85. package/dist/factions/profile-faction.d.ts +20 -8
  86. package/dist/factions/profile-faction.d.ts.map +1 -1
  87. package/dist/factions/profile-faction.js +20 -8
  88. package/dist/factions/profile-faction.js.map +1 -1
  89. package/dist/fleets/actions/add-ship.d.ts +48 -0
  90. package/dist/fleets/actions/add-ship.d.ts.map +1 -0
  91. package/dist/fleets/actions/add-ship.js +136 -0
  92. package/dist/fleets/actions/add-ship.js.map +1 -0
  93. package/dist/fleets/actions/create-fleet.d.ts +36 -0
  94. package/dist/fleets/actions/create-fleet.d.ts.map +1 -0
  95. package/dist/fleets/actions/create-fleet.js +108 -0
  96. package/dist/fleets/actions/create-fleet.js.map +1 -0
  97. package/dist/fleets/actions/crew-transfer.d.ts +45 -0
  98. package/dist/fleets/actions/crew-transfer.d.ts.map +1 -0
  99. package/dist/fleets/actions/crew-transfer.js +99 -0
  100. package/dist/fleets/actions/crew-transfer.js.map +1 -0
  101. package/dist/fleets/actions/deployment-storage.d.ts +161 -0
  102. package/dist/fleets/actions/deployment-storage.d.ts.map +1 -0
  103. package/dist/fleets/actions/deployment-storage.js +297 -0
  104. package/dist/fleets/actions/deployment-storage.js.map +1 -0
  105. package/dist/fleets/actions/disband.d.ts +42 -0
  106. package/dist/fleets/actions/disband.d.ts.map +1 -0
  107. package/dist/fleets/actions/disband.js +107 -0
  108. package/dist/fleets/actions/disband.js.map +1 -0
  109. package/dist/fleets/actions/dock-bounds.d.ts +38 -0
  110. package/dist/fleets/actions/dock-bounds.d.ts.map +1 -0
  111. package/dist/fleets/actions/dock-bounds.js +46 -0
  112. package/dist/fleets/actions/dock-bounds.js.map +1 -0
  113. package/dist/fleets/actions/index.d.ts +90 -25
  114. package/dist/fleets/actions/index.d.ts.map +1 -1
  115. package/dist/fleets/actions/index.js +492 -353
  116. package/dist/fleets/actions/index.js.map +1 -1
  117. package/dist/fleets/actions/respawn.d.ts +38 -0
  118. package/dist/fleets/actions/respawn.d.ts.map +1 -0
  119. package/dist/fleets/actions/respawn.js +93 -0
  120. package/dist/fleets/actions/respawn.js.map +1 -0
  121. package/dist/fleets/actions/ship-import.d.ts +1 -1
  122. package/dist/fleets/actions/ship-import.d.ts.map +1 -1
  123. package/dist/fleets/actions/ship-import.js +61 -90
  124. package/dist/fleets/actions/ship-import.js.map +1 -1
  125. package/dist/fleets/actions/sync.d.ts +24 -0
  126. package/dist/fleets/actions/sync.d.ts.map +1 -0
  127. package/dist/fleets/actions/sync.js +55 -0
  128. package/dist/fleets/actions/sync.js.map +1 -0
  129. package/dist/fleets/composition-stats.d.ts +101 -0
  130. package/dist/fleets/composition-stats.d.ts.map +1 -0
  131. package/dist/fleets/composition-stats.js +354 -0
  132. package/dist/fleets/composition-stats.js.map +1 -0
  133. package/dist/fleets/cooldown.d.ts +47 -0
  134. package/dist/fleets/cooldown.d.ts.map +1 -0
  135. package/dist/fleets/cooldown.js +48 -0
  136. package/dist/fleets/cooldown.js.map +1 -0
  137. package/dist/fleets/coordinate-warp-estimate.d.ts +70 -0
  138. package/dist/fleets/coordinate-warp-estimate.d.ts.map +1 -0
  139. package/dist/fleets/coordinate-warp-estimate.js +46 -0
  140. package/dist/fleets/coordinate-warp-estimate.js.map +1 -0
  141. package/dist/fleets/index.d.ts +36 -0
  142. package/dist/fleets/index.d.ts.map +1 -1
  143. package/dist/fleets/index.js +55 -12
  144. package/dist/fleets/index.js.map +1 -1
  145. package/dist/fleets/movement-arithmetic.d.ts +21 -0
  146. package/dist/fleets/movement-arithmetic.d.ts.map +1 -0
  147. package/dist/fleets/movement-arithmetic.js +45 -0
  148. package/dist/fleets/movement-arithmetic.js.map +1 -0
  149. package/dist/fleets/subwarp-estimate.d.ts +72 -0
  150. package/dist/fleets/subwarp-estimate.d.ts.map +1 -0
  151. package/dist/fleets/subwarp-estimate.js +40 -0
  152. package/dist/fleets/subwarp-estimate.js.map +1 -0
  153. package/dist/fleets/travel-profile.d.ts +102 -0
  154. package/dist/fleets/travel-profile.d.ts.map +1 -0
  155. package/dist/fleets/travel-profile.js +120 -0
  156. package/dist/fleets/travel-profile.js.map +1 -0
  157. package/dist/fleets/warp-lane-estimate.d.ts +59 -0
  158. package/dist/fleets/warp-lane-estimate.d.ts.map +1 -0
  159. package/dist/fleets/warp-lane-estimate.js +36 -0
  160. package/dist/fleets/warp-lane-estimate.js.map +1 -0
  161. package/dist/fleets/warp-lane-policy.d.ts +74 -0
  162. package/dist/fleets/warp-lane-policy.d.ts.map +1 -0
  163. package/dist/fleets/warp-lane-policy.js +103 -0
  164. package/dist/fleets/warp-lane-policy.js.map +1 -0
  165. package/dist/fleets/warp-lane-toll.d.ts +55 -0
  166. package/dist/fleets/warp-lane-toll.d.ts.map +1 -0
  167. package/dist/fleets/warp-lane-toll.js +79 -0
  168. package/dist/fleets/warp-lane-toll.js.map +1 -0
  169. package/dist/identity/actions/create-profile.d.ts +1 -1
  170. package/dist/identity/actions/create-profile.d.ts.map +1 -1
  171. package/dist/identity/actions/create-profile.js +2 -2
  172. package/dist/identity/actions/create-profile.js.map +1 -1
  173. package/dist/identity/actions/index.d.ts +5 -4
  174. package/dist/identity/actions/index.d.ts.map +1 -1
  175. package/dist/identity/actions/index.js +52 -29
  176. package/dist/identity/actions/index.js.map +1 -1
  177. package/dist/identity/actions/research-unlock.d.ts +61 -0
  178. package/dist/identity/actions/research-unlock.d.ts.map +1 -0
  179. package/dist/identity/actions/research-unlock.js +166 -0
  180. package/dist/identity/actions/research-unlock.js.map +1 -0
  181. package/dist/identity/index.d.ts +63 -2
  182. package/dist/identity/index.d.ts.map +1 -1
  183. package/dist/identity/index.js +64 -12
  184. package/dist/identity/index.js.map +1 -1
  185. package/dist/identity/research-state.d.ts +60 -0
  186. package/dist/identity/research-state.d.ts.map +1 -0
  187. package/dist/identity/research-state.js +68 -0
  188. package/dist/identity/research-state.js.map +1 -0
  189. package/dist/index.d.ts +26 -1
  190. package/dist/index.d.ts.map +1 -1
  191. package/dist/index.js +24 -7
  192. package/dist/index.js.map +1 -1
  193. package/dist/internal/c4/account-decoding.d.ts +49 -0
  194. package/dist/internal/c4/account-decoding.d.ts.map +1 -0
  195. package/dist/internal/c4/account-decoding.js +74 -0
  196. package/dist/internal/c4/account-decoding.js.map +1 -0
  197. package/dist/internal/c4/career-xp.d.ts +58 -0
  198. package/dist/internal/c4/career-xp.d.ts.map +1 -0
  199. package/dist/internal/c4/career-xp.js +80 -0
  200. package/dist/internal/c4/career-xp.js.map +1 -0
  201. package/dist/internal/c4/cargo-bytes-config.d.ts +7 -0
  202. package/dist/internal/c4/cargo-bytes-config.d.ts.map +1 -0
  203. package/dist/internal/c4/cargo-bytes-config.js +19 -0
  204. package/dist/internal/c4/cargo-bytes-config.js.map +1 -0
  205. package/dist/internal/c4/cargo-instructions.d.ts +24 -0
  206. package/dist/internal/c4/cargo-instructions.d.ts.map +1 -0
  207. package/dist/internal/c4/cargo-instructions.js +30 -0
  208. package/dist/internal/c4/cargo-instructions.js.map +1 -0
  209. package/dist/internal/c4/cargo-pod.d.ts +15 -0
  210. package/dist/internal/c4/cargo-pod.d.ts.map +1 -0
  211. package/dist/internal/c4/cargo-pod.js +13 -0
  212. package/dist/internal/c4/cargo-pod.js.map +1 -0
  213. package/dist/internal/c4/character-modifier.d.ts +1 -1
  214. package/dist/internal/c4/character-modifier.d.ts.map +1 -1
  215. package/dist/internal/c4/character-registration.d.ts +2 -0
  216. package/dist/internal/c4/character-registration.d.ts.map +1 -1
  217. package/dist/internal/c4/character-registration.js +2 -0
  218. package/dist/internal/c4/character-registration.js.map +1 -1
  219. package/dist/internal/c4/character.d.ts +157 -0
  220. package/dist/internal/c4/character.d.ts.map +1 -0
  221. package/dist/internal/c4/character.js +64 -0
  222. package/dist/internal/c4/character.js.map +1 -0
  223. package/dist/internal/c4/claim-stake-building-definitions.d.ts.map +1 -1
  224. package/dist/internal/c4/claim-stake-building-definitions.js +2 -1
  225. package/dist/internal/c4/claim-stake-building-definitions.js.map +1 -1
  226. package/dist/internal/c4/claim-stake-configuration.d.ts.map +1 -1
  227. package/dist/internal/c4/claim-stake-configuration.js +5 -36
  228. package/dist/internal/c4/claim-stake-configuration.js.map +1 -1
  229. package/dist/internal/c4/claim-stake-definitions.d.ts +8 -4
  230. package/dist/internal/c4/claim-stake-definitions.d.ts.map +1 -1
  231. package/dist/internal/c4/claim-stake-definitions.js +6 -2
  232. package/dist/internal/c4/claim-stake-definitions.js.map +1 -1
  233. package/dist/internal/c4/claim-stake-plots.d.ts +17 -0
  234. package/dist/internal/c4/claim-stake-plots.d.ts.map +1 -0
  235. package/dist/internal/c4/claim-stake-plots.js +29 -0
  236. package/dist/internal/c4/claim-stake-plots.js.map +1 -0
  237. package/dist/internal/c4/claim-stake-standing.d.ts +80 -0
  238. package/dist/internal/c4/claim-stake-standing.d.ts.map +1 -0
  239. package/dist/internal/c4/claim-stake-standing.js +61 -0
  240. package/dist/internal/c4/claim-stake-standing.js.map +1 -0
  241. package/dist/internal/c4/claim-stake-storage.d.ts +45 -0
  242. package/dist/internal/c4/claim-stake-storage.d.ts.map +1 -0
  243. package/dist/internal/c4/claim-stake-storage.js +207 -0
  244. package/dist/internal/c4/claim-stake-storage.js.map +1 -0
  245. package/dist/internal/c4/claim-stake.d.ts +13 -33
  246. package/dist/internal/c4/claim-stake.d.ts.map +1 -1
  247. package/dist/internal/c4/claim-stake.js +49 -94
  248. package/dist/internal/c4/claim-stake.js.map +1 -1
  249. package/dist/internal/c4/combat-storage.d.ts +20 -0
  250. package/dist/internal/c4/combat-storage.d.ts.map +1 -0
  251. package/dist/internal/c4/combat-storage.js +43 -0
  252. package/dist/internal/c4/combat-storage.js.map +1 -0
  253. package/dist/internal/c4/combat.d.ts +1 -82
  254. package/dist/internal/c4/combat.d.ts.map +1 -1
  255. package/dist/internal/c4/combat.js +16 -213
  256. package/dist/internal/c4/combat.js.map +1 -1
  257. package/dist/internal/c4/crafting-process-codec.d.ts +13 -0
  258. package/dist/internal/c4/crafting-process-codec.d.ts.map +1 -0
  259. package/dist/internal/c4/crafting-process-codec.js +34 -0
  260. package/dist/internal/c4/crafting-process-codec.js.map +1 -0
  261. package/dist/internal/c4/crafting-storage.d.ts +20 -0
  262. package/dist/internal/c4/crafting-storage.d.ts.map +1 -0
  263. package/dist/internal/c4/crafting-storage.js +54 -0
  264. package/dist/internal/c4/crafting-storage.js.map +1 -0
  265. package/dist/internal/c4/crafting.d.ts +33 -33
  266. package/dist/internal/c4/crafting.d.ts.map +1 -1
  267. package/dist/internal/c4/crafting.js +76 -127
  268. package/dist/internal/c4/crafting.js.map +1 -1
  269. package/dist/internal/c4/currency-cache.d.ts +6 -0
  270. package/dist/internal/c4/currency-cache.d.ts.map +1 -0
  271. package/dist/internal/c4/currency-cache.js +27 -0
  272. package/dist/internal/c4/currency-cache.js.map +1 -0
  273. package/dist/internal/c4/discriminators.d.ts +93 -0
  274. package/dist/internal/c4/discriminators.d.ts.map +1 -0
  275. package/dist/internal/c4/discriminators.js +190 -0
  276. package/dist/internal/c4/discriminators.js.map +1 -0
  277. package/dist/internal/c4/docking-funding.d.ts +4 -0
  278. package/dist/internal/c4/docking-funding.d.ts.map +1 -0
  279. package/dist/internal/c4/docking-funding.js +11 -0
  280. package/dist/internal/c4/docking-funding.js.map +1 -0
  281. package/dist/internal/c4/faction-epoch-meter.d.ts +10 -0
  282. package/dist/internal/c4/faction-epoch-meter.d.ts.map +1 -0
  283. package/dist/internal/c4/faction-epoch-meter.js +26 -0
  284. package/dist/internal/c4/faction-epoch-meter.js.map +1 -0
  285. package/dist/internal/c4/faction-treasury.d.ts +23 -0
  286. package/dist/internal/c4/faction-treasury.d.ts.map +1 -0
  287. package/dist/internal/c4/faction-treasury.js +41 -0
  288. package/dist/internal/c4/faction-treasury.js.map +1 -0
  289. package/dist/internal/c4/faction.d.ts +21 -0
  290. package/dist/internal/c4/faction.d.ts.map +1 -0
  291. package/dist/internal/c4/faction.js +23 -0
  292. package/dist/internal/c4/faction.js.map +1 -0
  293. package/dist/internal/c4/factions.d.ts +40 -60
  294. package/dist/internal/c4/factions.d.ts.map +1 -1
  295. package/dist/internal/c4/factions.js +106 -225
  296. package/dist/internal/c4/factions.js.map +1 -1
  297. package/dist/internal/c4/fleet-arrival.d.ts +28 -0
  298. package/dist/internal/c4/fleet-arrival.d.ts.map +1 -0
  299. package/dist/internal/c4/fleet-arrival.js +42 -0
  300. package/dist/internal/c4/fleet-arrival.js.map +1 -0
  301. package/dist/internal/c4/fleet-creation.d.ts +14 -0
  302. package/dist/internal/c4/fleet-creation.d.ts.map +1 -0
  303. package/dist/internal/c4/fleet-creation.js +177 -0
  304. package/dist/internal/c4/fleet-creation.js.map +1 -0
  305. package/dist/internal/c4/fleet-crew-binding.d.ts +9 -0
  306. package/dist/internal/c4/fleet-crew-binding.d.ts.map +1 -1
  307. package/dist/internal/c4/fleet-crew-binding.js +31 -44
  308. package/dist/internal/c4/fleet-crew-binding.js.map +1 -1
  309. package/dist/internal/c4/fleet-disband-layout.d.ts +7 -0
  310. package/dist/internal/c4/fleet-disband-layout.d.ts.map +1 -0
  311. package/dist/internal/c4/fleet-disband-layout.js +7 -0
  312. package/dist/internal/c4/fleet-disband-layout.js.map +1 -0
  313. package/dist/internal/c4/fleet-disband.d.ts +41 -0
  314. package/dist/internal/c4/fleet-disband.d.ts.map +1 -0
  315. package/dist/internal/c4/fleet-disband.js +86 -0
  316. package/dist/internal/c4/fleet-disband.js.map +1 -0
  317. package/dist/internal/c4/fleet-points.d.ts +3 -0
  318. package/dist/internal/c4/fleet-points.d.ts.map +1 -0
  319. package/dist/internal/c4/fleet-points.js +8 -0
  320. package/dist/internal/c4/fleet-points.js.map +1 -0
  321. package/dist/internal/c4/fleet-respawn.d.ts +17 -0
  322. package/dist/internal/c4/fleet-respawn.d.ts.map +1 -0
  323. package/dist/internal/c4/fleet-respawn.js +62 -0
  324. package/dist/internal/c4/fleet-respawn.js.map +1 -0
  325. package/dist/internal/c4/fleet-ship-assembly.d.ts +26 -0
  326. package/dist/internal/c4/fleet-ship-assembly.d.ts.map +1 -0
  327. package/dist/internal/c4/fleet-ship-assembly.js +96 -0
  328. package/dist/internal/c4/fleet-ship-assembly.js.map +1 -0
  329. package/dist/internal/c4/fleet-storage.d.ts +44 -0
  330. package/dist/internal/c4/fleet-storage.d.ts.map +1 -0
  331. package/dist/internal/c4/fleet-storage.js +108 -0
  332. package/dist/internal/c4/fleet-storage.js.map +1 -0
  333. package/dist/internal/c4/fleet-sync.d.ts +5 -0
  334. package/dist/internal/c4/fleet-sync.d.ts.map +1 -0
  335. package/dist/internal/c4/fleet-sync.js +15 -0
  336. package/dist/internal/c4/fleet-sync.js.map +1 -0
  337. package/dist/internal/c4/fleet.d.ts +65 -24
  338. package/dist/internal/c4/fleet.d.ts.map +1 -1
  339. package/dist/internal/c4/fleet.js +50 -91
  340. package/dist/internal/c4/fleet.js.map +1 -1
  341. package/dist/internal/c4/game-definition-layout.d.ts +2 -0
  342. package/dist/internal/c4/game-definition-layout.d.ts.map +1 -1
  343. package/dist/internal/c4/game-definition-layout.js +4 -0
  344. package/dist/internal/c4/game-definition-layout.js.map +1 -1
  345. package/dist/internal/c4/game.d.ts +122 -32
  346. package/dist/internal/c4/game.d.ts.map +1 -1
  347. package/dist/internal/c4/game.js +229 -130
  348. package/dist/internal/c4/game.js.map +1 -1
  349. package/dist/internal/c4/identity.d.ts +3 -152
  350. package/dist/internal/c4/identity.d.ts.map +1 -1
  351. package/dist/internal/c4/identity.js +19 -126
  352. package/dist/internal/c4/identity.js.map +1 -1
  353. package/dist/internal/c4/local-market-order.d.ts +29 -0
  354. package/dist/internal/c4/local-market-order.d.ts.map +1 -0
  355. package/dist/internal/c4/local-market-order.js +46 -0
  356. package/dist/internal/c4/local-market-order.js.map +1 -0
  357. package/dist/internal/c4/loot-layout.js +1 -1
  358. package/dist/internal/c4/loot-layout.js.map +1 -1
  359. package/dist/internal/c4/loot.d.ts +89 -0
  360. package/dist/internal/c4/loot.d.ts.map +1 -0
  361. package/dist/internal/c4/loot.js +109 -0
  362. package/dist/internal/c4/loot.js.map +1 -0
  363. package/dist/internal/c4/loyalty.d.ts +7 -30
  364. package/dist/internal/c4/loyalty.d.ts.map +1 -1
  365. package/dist/internal/c4/loyalty.js +41 -163
  366. package/dist/internal/c4/loyalty.js.map +1 -1
  367. package/dist/internal/c4/market-storage.d.ts +25 -0
  368. package/dist/internal/c4/market-storage.d.ts.map +1 -0
  369. package/dist/internal/c4/market-storage.js +53 -0
  370. package/dist/internal/c4/market-storage.js.map +1 -0
  371. package/dist/internal/c4/market.d.ts +5 -38
  372. package/dist/internal/c4/market.d.ts.map +1 -1
  373. package/dist/internal/c4/market.js +26 -120
  374. package/dist/internal/c4/market.js.map +1 -1
  375. package/dist/internal/c4/mining-instructions.d.ts +2 -0
  376. package/dist/internal/c4/mining-instructions.d.ts.map +1 -0
  377. package/dist/internal/c4/mining-instructions.js +3 -0
  378. package/dist/internal/c4/mining-instructions.js.map +1 -0
  379. package/dist/internal/c4/open-signal.d.ts +8 -0
  380. package/dist/internal/c4/open-signal.d.ts.map +1 -0
  381. package/dist/internal/c4/open-signal.js +37 -0
  382. package/dist/internal/c4/open-signal.js.map +1 -0
  383. package/dist/internal/c4/pda.d.ts +49 -1
  384. package/dist/internal/c4/pda.d.ts.map +1 -1
  385. package/dist/internal/c4/pda.js +122 -17
  386. package/dist/internal/c4/pda.js.map +1 -1
  387. package/dist/internal/c4/profile-faction.d.ts +2 -1
  388. package/dist/internal/c4/profile-faction.d.ts.map +1 -1
  389. package/dist/internal/c4/profile-faction.js +36 -72
  390. package/dist/internal/c4/profile-faction.js.map +1 -1
  391. package/dist/internal/c4/profile-permissions.d.ts +1 -1
  392. package/dist/internal/c4/profile-permissions.d.ts.map +1 -1
  393. package/dist/internal/c4/profile-permissions.js +17 -1
  394. package/dist/internal/c4/profile-permissions.js.map +1 -1
  395. package/dist/internal/c4/region-geometry.d.ts +8 -0
  396. package/dist/internal/c4/region-geometry.d.ts.map +1 -0
  397. package/dist/internal/c4/region-geometry.js +43 -0
  398. package/dist/internal/c4/region-geometry.js.map +1 -0
  399. package/dist/internal/c4/rewards.d.ts +1 -61
  400. package/dist/internal/c4/rewards.d.ts.map +1 -1
  401. package/dist/internal/c4/rewards.js +23 -203
  402. package/dist/internal/c4/rewards.js.map +1 -1
  403. package/dist/internal/c4/runtime-bindings.d.ts +4 -0
  404. package/dist/internal/c4/runtime-bindings.d.ts.map +1 -0
  405. package/dist/internal/c4/runtime-bindings.js +4 -0
  406. package/dist/internal/c4/runtime-bindings.js.map +1 -0
  407. package/dist/internal/c4/scan-detection-codecs.d.ts +3 -0
  408. package/dist/internal/c4/scan-detection-codecs.d.ts.map +1 -0
  409. package/dist/internal/c4/scan-detection-codecs.js +3 -0
  410. package/dist/internal/c4/scan-detection-codecs.js.map +1 -0
  411. package/dist/internal/c4/scan-lifecycle.d.ts +324 -0
  412. package/dist/internal/c4/scan-lifecycle.d.ts.map +1 -0
  413. package/dist/internal/c4/scan-lifecycle.js +249 -0
  414. package/dist/internal/c4/scan-lifecycle.js.map +1 -0
  415. package/dist/internal/c4/scan-storage.d.ts +7 -0
  416. package/dist/internal/c4/scan-storage.d.ts.map +1 -0
  417. package/dist/internal/c4/scan-storage.js +10 -0
  418. package/dist/internal/c4/scan-storage.js.map +1 -0
  419. package/dist/internal/c4/scanning.d.ts +4 -41
  420. package/dist/internal/c4/scanning.d.ts.map +1 -1
  421. package/dist/internal/c4/scanning.js +27 -108
  422. package/dist/internal/c4/scanning.js.map +1 -1
  423. package/dist/internal/c4/ship-import.d.ts +4 -0
  424. package/dist/internal/c4/ship-import.d.ts.map +1 -0
  425. package/dist/internal/c4/ship-import.js +13 -0
  426. package/dist/internal/c4/ship-import.js.map +1 -0
  427. package/dist/internal/c4/ship-stats.d.ts.map +1 -1
  428. package/dist/internal/c4/ship-stats.js +46 -44
  429. package/dist/internal/c4/ship-stats.js.map +1 -1
  430. package/dist/internal/c4/starbase.d.ts +0 -32
  431. package/dist/internal/c4/starbase.d.ts.map +1 -1
  432. package/dist/internal/c4/starbase.js +26 -136
  433. package/dist/internal/c4/starbase.js.map +1 -1
  434. package/dist/internal/c4/stimulant.d.ts +26 -0
  435. package/dist/internal/c4/stimulant.d.ts.map +1 -0
  436. package/dist/internal/c4/stimulant.js +36 -0
  437. package/dist/internal/c4/stimulant.js.map +1 -0
  438. package/dist/internal/c4/wallet-cargo-cache.js +2 -1
  439. package/dist/internal/c4/wallet-cargo-cache.js.map +1 -1
  440. package/dist/internal/c4/world.d.ts +40 -7
  441. package/dist/internal/c4/world.d.ts.map +1 -1
  442. package/dist/internal/c4/world.js +87 -129
  443. package/dist/internal/c4/world.js.map +1 -1
  444. package/dist/internal/cargo.d.ts +2 -0
  445. package/dist/internal/cargo.d.ts.map +1 -1
  446. package/dist/internal/cargo.js +8 -0
  447. package/dist/internal/cargo.js.map +1 -1
  448. package/dist/internal/fleet-route.d.ts +12 -0
  449. package/dist/internal/fleet-route.d.ts.map +1 -0
  450. package/dist/internal/fleet-route.js +11 -0
  451. package/dist/internal/fleet-route.js.map +1 -0
  452. package/dist/internal/fleet.d.ts +3 -2
  453. package/dist/internal/fleet.d.ts.map +1 -1
  454. package/dist/internal/fleet.js +3 -5
  455. package/dist/internal/fleet.js.map +1 -1
  456. package/dist/internal/loyalty-atlas-bank-state.d.ts +17 -0
  457. package/dist/internal/loyalty-atlas-bank-state.d.ts.map +1 -0
  458. package/dist/internal/loyalty-atlas-bank-state.js +11 -0
  459. package/dist/internal/loyalty-atlas-bank-state.js.map +1 -0
  460. package/dist/internal/planning/account-state.d.ts +6 -0
  461. package/dist/internal/planning/account-state.d.ts.map +1 -1
  462. package/dist/internal/planning/account-state.js +13 -0
  463. package/dist/internal/planning/account-state.js.map +1 -1
  464. package/dist/internal/planning/action-runtime.d.ts +38 -0
  465. package/dist/internal/planning/action-runtime.d.ts.map +1 -0
  466. package/dist/internal/planning/action-runtime.js +63 -0
  467. package/dist/internal/planning/action-runtime.js.map +1 -0
  468. package/dist/internal/planning/arrival-guards.d.ts +19 -0
  469. package/dist/internal/planning/arrival-guards.d.ts.map +1 -0
  470. package/dist/internal/planning/arrival-guards.js +172 -0
  471. package/dist/internal/planning/arrival-guards.js.map +1 -0
  472. package/dist/internal/planning/authorization-adapter.d.ts +61 -39
  473. package/dist/internal/planning/authorization-adapter.d.ts.map +1 -1
  474. package/dist/internal/planning/authorization-adapter.js +219 -83
  475. package/dist/internal/planning/authorization-adapter.js.map +1 -1
  476. package/dist/internal/planning/cargo-storage-actions.d.ts +8 -0
  477. package/dist/internal/planning/cargo-storage-actions.d.ts.map +1 -0
  478. package/dist/internal/planning/cargo-storage-actions.js +97 -0
  479. package/dist/internal/planning/cargo-storage-actions.js.map +1 -0
  480. package/dist/internal/planning/character-access.d.ts +28 -0
  481. package/dist/internal/planning/character-access.d.ts.map +1 -0
  482. package/dist/internal/planning/character-access.js +41 -0
  483. package/dist/internal/planning/character-access.js.map +1 -0
  484. package/dist/internal/planning/character-registration-guard.d.ts +4 -0
  485. package/dist/internal/planning/character-registration-guard.d.ts.map +1 -0
  486. package/dist/internal/planning/character-registration-guard.js +59 -0
  487. package/dist/internal/planning/character-registration-guard.js.map +1 -0
  488. package/dist/internal/planning/character-registration.d.ts +23 -0
  489. package/dist/internal/planning/character-registration.d.ts.map +1 -0
  490. package/dist/internal/planning/character-registration.js +87 -0
  491. package/dist/internal/planning/character-registration.js.map +1 -0
  492. package/dist/internal/planning/claim-stake-storage-actions.d.ts +12 -0
  493. package/dist/internal/planning/claim-stake-storage-actions.d.ts.map +1 -0
  494. package/dist/internal/planning/claim-stake-storage-actions.js +63 -0
  495. package/dist/internal/planning/claim-stake-storage-actions.js.map +1 -0
  496. package/dist/internal/planning/combat-storage-actions.d.ts +9 -0
  497. package/dist/internal/planning/combat-storage-actions.d.ts.map +1 -0
  498. package/dist/internal/planning/combat-storage-actions.js +55 -0
  499. package/dist/internal/planning/combat-storage-actions.js.map +1 -0
  500. package/dist/internal/planning/crafting-storage-actions.d.ts +7 -0
  501. package/dist/internal/planning/crafting-storage-actions.d.ts.map +1 -0
  502. package/dist/internal/planning/crafting-storage-actions.js +64 -0
  503. package/dist/internal/planning/crafting-storage-actions.js.map +1 -0
  504. package/dist/internal/planning/currency-cache-guard.d.ts +6 -0
  505. package/dist/internal/planning/currency-cache-guard.d.ts.map +1 -0
  506. package/dist/internal/planning/currency-cache-guard.js +77 -0
  507. package/dist/internal/planning/currency-cache-guard.js.map +1 -0
  508. package/dist/internal/planning/democracy-history.d.ts +50 -0
  509. package/dist/internal/planning/democracy-history.d.ts.map +1 -0
  510. package/dist/internal/planning/democracy-history.js +189 -0
  511. package/dist/internal/planning/democracy-history.js.map +1 -0
  512. package/dist/internal/planning/direct-options.d.ts +17 -0
  513. package/dist/internal/planning/direct-options.d.ts.map +1 -0
  514. package/dist/internal/planning/direct-options.js +43 -0
  515. package/dist/internal/planning/direct-options.js.map +1 -0
  516. package/dist/internal/planning/disband-storage-actions.d.ts +9 -0
  517. package/dist/internal/planning/disband-storage-actions.d.ts.map +1 -0
  518. package/dist/internal/planning/disband-storage-actions.js +24 -0
  519. package/dist/internal/planning/disband-storage-actions.js.map +1 -0
  520. package/dist/internal/planning/faction-enrollment.d.ts +0 -4
  521. package/dist/internal/planning/faction-enrollment.d.ts.map +1 -1
  522. package/dist/internal/planning/faction-enrollment.js +1 -31
  523. package/dist/internal/planning/faction-enrollment.js.map +1 -1
  524. package/dist/internal/planning/faction-trade-guards.d.ts +30 -0
  525. package/dist/internal/planning/faction-trade-guards.d.ts.map +1 -0
  526. package/dist/internal/planning/faction-trade-guards.js +15 -0
  527. package/dist/internal/planning/faction-trade-guards.js.map +1 -0
  528. package/dist/internal/planning/faction-trade-state.d.ts +16 -0
  529. package/dist/internal/planning/faction-trade-state.d.ts.map +1 -0
  530. package/dist/internal/planning/faction-trade-state.js +43 -0
  531. package/dist/internal/planning/faction-trade-state.js.map +1 -0
  532. package/dist/internal/planning/fleet-arrival.d.ts +40 -0
  533. package/dist/internal/planning/fleet-arrival.d.ts.map +1 -0
  534. package/dist/internal/planning/fleet-arrival.js +107 -0
  535. package/dist/internal/planning/fleet-arrival.js.map +1 -0
  536. package/dist/internal/planning/fleet-attack-xp.d.ts +20 -0
  537. package/dist/internal/planning/fleet-attack-xp.d.ts.map +1 -0
  538. package/dist/internal/planning/fleet-attack-xp.js +104 -0
  539. package/dist/internal/planning/fleet-attack-xp.js.map +1 -0
  540. package/dist/internal/planning/fleet-creation.d.ts +30 -0
  541. package/dist/internal/planning/fleet-creation.d.ts.map +1 -0
  542. package/dist/internal/planning/fleet-creation.js +89 -0
  543. package/dist/internal/planning/fleet-creation.js.map +1 -0
  544. package/dist/internal/planning/fleet-crew-guard.d.ts +29 -0
  545. package/dist/internal/planning/fleet-crew-guard.d.ts.map +1 -0
  546. package/dist/internal/planning/fleet-crew-guard.js +48 -0
  547. package/dist/internal/planning/fleet-crew-guard.js.map +1 -0
  548. package/dist/internal/planning/fleet-crew-transfer.d.ts +7 -0
  549. package/dist/internal/planning/fleet-crew-transfer.d.ts.map +1 -0
  550. package/dist/internal/planning/fleet-crew-transfer.js +152 -0
  551. package/dist/internal/planning/fleet-crew-transfer.js.map +1 -0
  552. package/dist/internal/planning/fleet-disband-guard.d.ts +12 -0
  553. package/dist/internal/planning/fleet-disband-guard.d.ts.map +1 -0
  554. package/dist/internal/planning/fleet-disband-guard.js +54 -0
  555. package/dist/internal/planning/fleet-disband-guard.js.map +1 -0
  556. package/dist/internal/planning/fleet-disband.d.ts +36 -0
  557. package/dist/internal/planning/fleet-disband.d.ts.map +1 -0
  558. package/dist/internal/planning/fleet-disband.js +82 -0
  559. package/dist/internal/planning/fleet-disband.js.map +1 -0
  560. package/dist/internal/planning/fleet-dock-funding-guard.d.ts +42 -0
  561. package/dist/internal/planning/fleet-dock-funding-guard.d.ts.map +1 -0
  562. package/dist/internal/planning/fleet-dock-funding-guard.js +216 -0
  563. package/dist/internal/planning/fleet-dock-funding-guard.js.map +1 -0
  564. package/dist/internal/planning/fleet-dock-funding.d.ts +9 -0
  565. package/dist/internal/planning/fleet-dock-funding.d.ts.map +1 -0
  566. package/dist/internal/planning/fleet-dock-funding.js +164 -0
  567. package/dist/internal/planning/fleet-dock-funding.js.map +1 -0
  568. package/dist/internal/planning/fleet-docking-guard.d.ts +4 -0
  569. package/dist/internal/planning/fleet-docking-guard.d.ts.map +1 -0
  570. package/dist/internal/planning/fleet-docking-guard.js +44 -0
  571. package/dist/internal/planning/fleet-docking-guard.js.map +1 -0
  572. package/dist/internal/planning/fleet-docking.d.ts +25 -0
  573. package/dist/internal/planning/fleet-docking.d.ts.map +1 -0
  574. package/dist/internal/planning/fleet-docking.js +106 -0
  575. package/dist/internal/planning/fleet-docking.js.map +1 -0
  576. package/dist/internal/planning/fleet-rental.d.ts +5 -0
  577. package/dist/internal/planning/fleet-rental.d.ts.map +1 -0
  578. package/dist/internal/planning/fleet-rental.js +9 -0
  579. package/dist/internal/planning/fleet-rental.js.map +1 -0
  580. package/dist/internal/planning/fleet-respawn.d.ts +28 -0
  581. package/dist/internal/planning/fleet-respawn.d.ts.map +1 -0
  582. package/dist/internal/planning/fleet-respawn.js +164 -0
  583. package/dist/internal/planning/fleet-respawn.js.map +1 -0
  584. package/dist/internal/planning/fleet-ship-assembly.d.ts +38 -0
  585. package/dist/internal/planning/fleet-ship-assembly.d.ts.map +1 -0
  586. package/dist/internal/planning/fleet-ship-assembly.js +167 -0
  587. package/dist/internal/planning/fleet-ship-assembly.js.map +1 -0
  588. package/dist/internal/planning/fleet-state.d.ts +25 -0
  589. package/dist/internal/planning/fleet-state.d.ts.map +1 -0
  590. package/dist/internal/planning/fleet-state.js +28 -0
  591. package/dist/internal/planning/fleet-state.js.map +1 -0
  592. package/dist/internal/planning/governance-fees.d.ts +64 -0
  593. package/dist/internal/planning/governance-fees.d.ts.map +1 -0
  594. package/dist/internal/planning/governance-fees.js +231 -0
  595. package/dist/internal/planning/governance-fees.js.map +1 -0
  596. package/dist/internal/planning/local-market-order-funding.d.ts +8 -0
  597. package/dist/internal/planning/local-market-order-funding.d.ts.map +1 -0
  598. package/dist/internal/planning/local-market-order-funding.js +128 -0
  599. package/dist/internal/planning/local-market-order-funding.js.map +1 -0
  600. package/dist/internal/planning/local-market-order.d.ts +40 -0
  601. package/dist/internal/planning/local-market-order.d.ts.map +1 -0
  602. package/dist/internal/planning/local-market-order.js +139 -0
  603. package/dist/internal/planning/local-market-order.js.map +1 -0
  604. package/dist/internal/planning/market-storage-actions.d.ts +7 -0
  605. package/dist/internal/planning/market-storage-actions.d.ts.map +1 -0
  606. package/dist/internal/planning/market-storage-actions.js +71 -0
  607. package/dist/internal/planning/market-storage-actions.js.map +1 -0
  608. package/dist/internal/planning/mining-stop.d.ts +14 -0
  609. package/dist/internal/planning/mining-stop.d.ts.map +1 -0
  610. package/dist/internal/planning/mining-stop.js +42 -0
  611. package/dist/internal/planning/mining-stop.js.map +1 -0
  612. package/dist/internal/planning/mining-storage-actions.d.ts +11 -0
  613. package/dist/internal/planning/mining-storage-actions.d.ts.map +1 -0
  614. package/dist/internal/planning/mining-storage-actions.js +57 -0
  615. package/dist/internal/planning/mining-storage-actions.js.map +1 -0
  616. package/dist/internal/planning/movement-storage-actions.d.ts +7 -0
  617. package/dist/internal/planning/movement-storage-actions.d.ts.map +1 -0
  618. package/dist/internal/planning/movement-storage-actions.js +38 -0
  619. package/dist/internal/planning/movement-storage-actions.js.map +1 -0
  620. package/dist/internal/planning/movement-xp-guard.d.ts +4 -0
  621. package/dist/internal/planning/movement-xp-guard.d.ts.map +1 -0
  622. package/dist/internal/planning/movement-xp-guard.js +104 -0
  623. package/dist/internal/planning/movement-xp-guard.js.map +1 -0
  624. package/dist/internal/planning/open-signal-guard.d.ts +3 -0
  625. package/dist/internal/planning/open-signal-guard.d.ts.map +1 -0
  626. package/dist/internal/planning/open-signal-guard.js +32 -0
  627. package/dist/internal/planning/open-signal-guard.js.map +1 -0
  628. package/dist/internal/planning/open-signal.d.ts +31 -0
  629. package/dist/internal/planning/open-signal.d.ts.map +1 -0
  630. package/dist/internal/planning/open-signal.js +94 -0
  631. package/dist/internal/planning/open-signal.js.map +1 -0
  632. package/dist/internal/planning/original-token.d.ts +40 -0
  633. package/dist/internal/planning/original-token.d.ts.map +1 -0
  634. package/dist/internal/planning/original-token.js +112 -0
  635. package/dist/internal/planning/original-token.js.map +1 -0
  636. package/dist/internal/planning/own-starbase-facts.d.ts +37 -0
  637. package/dist/internal/planning/own-starbase-facts.d.ts.map +1 -0
  638. package/dist/internal/planning/own-starbase-facts.js +52 -0
  639. package/dist/internal/planning/own-starbase-facts.js.map +1 -0
  640. package/dist/internal/planning/plan-fields.d.ts +60 -0
  641. package/dist/internal/planning/plan-fields.d.ts.map +1 -0
  642. package/dist/internal/planning/plan-fields.js +159 -0
  643. package/dist/internal/planning/plan-fields.js.map +1 -0
  644. package/dist/internal/planning/plan-guards.d.ts +4 -0
  645. package/dist/internal/planning/plan-guards.d.ts.map +1 -0
  646. package/dist/internal/planning/plan-guards.js +184 -0
  647. package/dist/internal/planning/plan-guards.js.map +1 -0
  648. package/dist/internal/planning/profile-key.d.ts +13 -0
  649. package/dist/internal/planning/profile-key.d.ts.map +1 -0
  650. package/dist/internal/planning/profile-key.js +20 -0
  651. package/dist/internal/planning/profile-key.js.map +1 -0
  652. package/dist/internal/planning/profile-storage-funding-guard.d.ts +29 -0
  653. package/dist/internal/planning/profile-storage-funding-guard.d.ts.map +1 -0
  654. package/dist/internal/planning/profile-storage-funding-guard.js +115 -0
  655. package/dist/internal/planning/profile-storage-funding-guard.js.map +1 -0
  656. package/dist/internal/planning/profile-storage-funding.d.ts +46 -0
  657. package/dist/internal/planning/profile-storage-funding.d.ts.map +1 -0
  658. package/dist/internal/planning/profile-storage-funding.js +58 -0
  659. package/dist/internal/planning/profile-storage-funding.js.map +1 -0
  660. package/dist/internal/planning/recipe-state.d.ts +9 -0
  661. package/dist/internal/planning/recipe-state.d.ts.map +1 -0
  662. package/dist/internal/planning/recipe-state.js +23 -0
  663. package/dist/internal/planning/recipe-state.js.map +1 -0
  664. package/dist/internal/planning/region-access.d.ts +30 -0
  665. package/dist/internal/planning/region-access.d.ts.map +1 -0
  666. package/dist/internal/planning/region-access.js +94 -0
  667. package/dist/internal/planning/region-access.js.map +1 -0
  668. package/dist/internal/planning/rent-catalog.d.ts +37 -0
  669. package/dist/internal/planning/rent-catalog.d.ts.map +1 -0
  670. package/dist/internal/planning/rent-catalog.js +165 -0
  671. package/dist/internal/planning/rent-catalog.js.map +1 -0
  672. package/dist/internal/planning/rent.d.ts +19 -0
  673. package/dist/internal/planning/rent.d.ts.map +1 -0
  674. package/dist/internal/planning/rent.js +35 -0
  675. package/dist/internal/planning/rent.js.map +1 -0
  676. package/dist/internal/planning/research-unlock.d.ts +69 -0
  677. package/dist/internal/planning/research-unlock.d.ts.map +1 -0
  678. package/dist/internal/planning/research-unlock.js +287 -0
  679. package/dist/internal/planning/research-unlock.js.map +1 -0
  680. package/dist/internal/planning/respawn-guards.d.ts +3 -0
  681. package/dist/internal/planning/respawn-guards.d.ts.map +1 -0
  682. package/dist/internal/planning/respawn-guards.js +42 -0
  683. package/dist/internal/planning/respawn-guards.js.map +1 -0
  684. package/dist/internal/planning/runtime-guards.d.ts +41 -0
  685. package/dist/internal/planning/runtime-guards.d.ts.map +1 -0
  686. package/dist/internal/planning/runtime-guards.js +658 -0
  687. package/dist/internal/planning/runtime-guards.js.map +1 -0
  688. package/dist/internal/planning/scan-completion-guard.d.ts +43 -0
  689. package/dist/internal/planning/scan-completion-guard.d.ts.map +1 -0
  690. package/dist/internal/planning/scan-completion-guard.js +95 -0
  691. package/dist/internal/planning/scan-completion-guard.js.map +1 -0
  692. package/dist/internal/planning/scan-completion.d.ts +20 -0
  693. package/dist/internal/planning/scan-completion.d.ts.map +1 -0
  694. package/dist/internal/planning/scan-completion.js +198 -0
  695. package/dist/internal/planning/scan-completion.js.map +1 -0
  696. package/dist/internal/planning/scan-detect-guard.d.ts +34 -0
  697. package/dist/internal/planning/scan-detect-guard.d.ts.map +1 -0
  698. package/dist/internal/planning/scan-detect-guard.js +77 -0
  699. package/dist/internal/planning/scan-detect-guard.js.map +1 -0
  700. package/dist/internal/planning/scan-detect.d.ts +24 -0
  701. package/dist/internal/planning/scan-detect.d.ts.map +1 -0
  702. package/dist/internal/planning/scan-detect.js +246 -0
  703. package/dist/internal/planning/scan-detect.js.map +1 -0
  704. package/dist/internal/planning/scan-idle-scope.d.ts +5 -0
  705. package/dist/internal/planning/scan-idle-scope.d.ts.map +1 -0
  706. package/dist/internal/planning/scan-idle-scope.js +5 -0
  707. package/dist/internal/planning/scan-idle-scope.js.map +1 -0
  708. package/dist/internal/planning/scan-input.d.ts +9 -0
  709. package/dist/internal/planning/scan-input.d.ts.map +1 -0
  710. package/dist/internal/planning/scan-input.js +58 -0
  711. package/dist/internal/planning/scan-input.js.map +1 -0
  712. package/dist/internal/planning/scan-storage-actions.d.ts +9 -0
  713. package/dist/internal/planning/scan-storage-actions.d.ts.map +1 -0
  714. package/dist/internal/planning/scan-storage-actions.js +27 -0
  715. package/dist/internal/planning/scan-storage-actions.js.map +1 -0
  716. package/dist/internal/planning/ship-import-funding.d.ts +22 -0
  717. package/dist/internal/planning/ship-import-funding.d.ts.map +1 -0
  718. package/dist/internal/planning/ship-import-funding.js +53 -0
  719. package/dist/internal/planning/ship-import-funding.js.map +1 -0
  720. package/dist/internal/planning/ship-token.d.ts +1 -6
  721. package/dist/internal/planning/ship-token.d.ts.map +1 -1
  722. package/dist/internal/planning/ship-token.js +0 -37
  723. package/dist/internal/planning/ship-token.js.map +1 -1
  724. package/dist/internal/planning/starbase-player-owner.d.ts +21 -0
  725. package/dist/internal/planning/starbase-player-owner.d.ts.map +1 -0
  726. package/dist/internal/planning/starbase-player-owner.js +17 -0
  727. package/dist/internal/planning/starbase-player-owner.js.map +1 -0
  728. package/dist/internal/planning/starbase-player-recovery-guard.d.ts +7 -0
  729. package/dist/internal/planning/starbase-player-recovery-guard.d.ts.map +1 -0
  730. package/dist/internal/planning/starbase-player-recovery-guard.js +39 -0
  731. package/dist/internal/planning/starbase-player-recovery-guard.js.map +1 -0
  732. package/dist/internal/planning/starbase-system-guard.d.ts +6 -0
  733. package/dist/internal/planning/starbase-system-guard.d.ts.map +1 -0
  734. package/dist/internal/planning/starbase-system-guard.js +94 -0
  735. package/dist/internal/planning/starbase-system-guard.js.map +1 -0
  736. package/dist/internal/planning/starbase-system.d.ts +15 -0
  737. package/dist/internal/planning/starbase-system.d.ts.map +1 -0
  738. package/dist/internal/planning/starbase-system.js +25 -0
  739. package/dist/internal/planning/starbase-system.js.map +1 -0
  740. package/dist/internal/planning/storage-action-readers.d.ts +10 -0
  741. package/dist/internal/planning/storage-action-readers.d.ts.map +1 -0
  742. package/dist/internal/planning/storage-action-readers.js +58 -0
  743. package/dist/internal/planning/storage-action-readers.js.map +1 -0
  744. package/dist/internal/planning/storage-actions.d.ts +89 -0
  745. package/dist/internal/planning/storage-actions.d.ts.map +1 -0
  746. package/dist/internal/planning/storage-actions.js +183 -0
  747. package/dist/internal/planning/storage-actions.js.map +1 -0
  748. package/dist/internal/planning/token-mint.d.ts.map +1 -1
  749. package/dist/internal/planning/token-mint.js +2 -1
  750. package/dist/internal/planning/token-mint.js.map +1 -1
  751. package/dist/internal/planning/trade-faction-market.d.ts +20 -0
  752. package/dist/internal/planning/trade-faction-market.d.ts.map +1 -0
  753. package/dist/internal/planning/trade-faction-market.js +56 -0
  754. package/dist/internal/planning/trade-faction-market.js.map +1 -0
  755. package/dist/internal/planning/wallet-starbase.d.ts +34 -0
  756. package/dist/internal/planning/wallet-starbase.d.ts.map +1 -0
  757. package/dist/internal/planning/wallet-starbase.js +39 -0
  758. package/dist/internal/planning/wallet-starbase.js.map +1 -0
  759. package/dist/internal/planning/xp-runtime.d.ts +36 -0
  760. package/dist/internal/planning/xp-runtime.d.ts.map +1 -0
  761. package/dist/internal/planning/xp-runtime.js +122 -0
  762. package/dist/internal/planning/xp-runtime.js.map +1 -0
  763. package/dist/internal/read-meta.d.ts +12 -0
  764. package/dist/internal/read-meta.d.ts.map +1 -1
  765. package/dist/internal/read-meta.js +10 -0
  766. package/dist/internal/read-meta.js.map +1 -1
  767. package/dist/internal/system-program.d.ts +4 -0
  768. package/dist/internal/system-program.d.ts.map +1 -0
  769. package/dist/internal/system-program.js +3 -0
  770. package/dist/internal/system-program.js.map +1 -0
  771. package/dist/loyalty/actions/index.d.ts +1 -1
  772. package/dist/loyalty/actions/index.d.ts.map +1 -1
  773. package/dist/loyalty/actions/index.js +16 -46
  774. package/dist/loyalty/actions/index.js.map +1 -1
  775. package/dist/loyalty/index.d.ts +4 -0
  776. package/dist/loyalty/index.d.ts.map +1 -1
  777. package/dist/loyalty/index.js +21 -33
  778. package/dist/loyalty/index.js.map +1 -1
  779. package/dist/markets/actions/index.d.ts +62 -4
  780. package/dist/markets/actions/index.d.ts.map +1 -1
  781. package/dist/markets/actions/index.js +261 -187
  782. package/dist/markets/actions/index.js.map +1 -1
  783. package/dist/markets/actions/trade-faction-market.d.ts +45 -0
  784. package/dist/markets/actions/trade-faction-market.d.ts.map +1 -0
  785. package/dist/markets/actions/trade-faction-market.js +311 -0
  786. package/dist/markets/actions/trade-faction-market.js.map +1 -0
  787. package/dist/markets/index.d.ts +38 -7
  788. package/dist/markets/index.d.ts.map +1 -1
  789. package/dist/markets/index.js +59 -20
  790. package/dist/markets/index.js.map +1 -1
  791. package/dist/mining/actions/index.d.ts +18 -15
  792. package/dist/mining/actions/index.d.ts.map +1 -1
  793. package/dist/mining/actions/index.js +146 -69
  794. package/dist/mining/actions/index.js.map +1 -1
  795. package/dist/mining/estimate-input.d.ts +103 -0
  796. package/dist/mining/estimate-input.d.ts.map +1 -0
  797. package/dist/mining/estimate-input.js +86 -0
  798. package/dist/mining/estimate-input.js.map +1 -0
  799. package/dist/mining/estimate.d.ts +114 -0
  800. package/dist/mining/estimate.d.ts.map +1 -0
  801. package/dist/mining/estimate.js +127 -0
  802. package/dist/mining/estimate.js.map +1 -0
  803. package/dist/mining/index.d.ts +10 -2
  804. package/dist/mining/index.d.ts.map +1 -1
  805. package/dist/mining/index.js +11 -13
  806. package/dist/mining/index.js.map +1 -1
  807. package/dist/mining/profile.d.ts +82 -0
  808. package/dist/mining/profile.d.ts.map +1 -0
  809. package/dist/mining/profile.js +109 -0
  810. package/dist/mining/profile.js.map +1 -0
  811. package/dist/planning/assembly.d.ts +6 -1
  812. package/dist/planning/assembly.d.ts.map +1 -1
  813. package/dist/planning/assembly.js +12 -8
  814. package/dist/planning/assembly.js.map +1 -1
  815. package/dist/planning/execution.d.ts +19 -3
  816. package/dist/planning/execution.d.ts.map +1 -1
  817. package/dist/planning/execution.js +623 -72
  818. package/dist/planning/execution.js.map +1 -1
  819. package/dist/planning/index.d.ts +252 -165
  820. package/dist/planning/index.d.ts.map +1 -1
  821. package/dist/planning/index.js +31 -388
  822. package/dist/planning/index.js.map +1 -1
  823. package/dist/planning/plan-construction.d.ts +25 -0
  824. package/dist/planning/plan-construction.d.ts.map +1 -0
  825. package/dist/planning/plan-construction.js +143 -0
  826. package/dist/planning/plan-construction.js.map +1 -0
  827. package/dist/planning/plan-sequence-checkpoint.d.ts +9 -0
  828. package/dist/planning/plan-sequence-checkpoint.d.ts.map +1 -1
  829. package/dist/planning/plan-sequence-checkpoint.js +16 -7
  830. package/dist/planning/plan-sequence-checkpoint.js.map +1 -1
  831. package/dist/planning/plan-sequence-execution.d.ts +5 -0
  832. package/dist/planning/plan-sequence-execution.d.ts.map +1 -1
  833. package/dist/planning/plan-sequence-execution.js +74 -40
  834. package/dist/planning/plan-sequence-execution.js.map +1 -1
  835. package/dist/planning/plan-sequence.d.ts +2 -1
  836. package/dist/planning/plan-sequence.d.ts.map +1 -1
  837. package/dist/planning/plan-sequence.js +8 -1
  838. package/dist/planning/plan-sequence.js.map +1 -1
  839. package/dist/planning/plan-validation.d.ts +17 -0
  840. package/dist/planning/plan-validation.d.ts.map +1 -0
  841. package/dist/planning/plan-validation.js +147 -0
  842. package/dist/planning/plan-validation.js.map +1 -0
  843. package/dist/planning/simulation.d.ts +25 -1
  844. package/dist/planning/simulation.d.ts.map +1 -1
  845. package/dist/planning/simulation.js +111 -11
  846. package/dist/planning/simulation.js.map +1 -1
  847. package/dist/registry/cargo.d.ts +7 -12
  848. package/dist/registry/cargo.d.ts.map +1 -1
  849. package/dist/registry/cargo.js +19 -20
  850. package/dist/registry/cargo.js.map +1 -1
  851. package/dist/registry/definition-section-snapshot.d.ts +1 -1
  852. package/dist/registry/definition-section-snapshot.d.ts.map +1 -1
  853. package/dist/registry/definition-section-snapshot.js +10 -3
  854. package/dist/registry/definition-section-snapshot.js.map +1 -1
  855. package/dist/registry/docking.d.ts +10 -0
  856. package/dist/registry/docking.d.ts.map +1 -0
  857. package/dist/registry/docking.js +12 -0
  858. package/dist/registry/docking.js.map +1 -0
  859. package/dist/registry/index.d.ts +35 -4
  860. package/dist/registry/index.d.ts.map +1 -1
  861. package/dist/registry/index.js +226 -227
  862. package/dist/registry/index.js.map +1 -1
  863. package/dist/registry/onboarding.d.ts +10 -2
  864. package/dist/registry/onboarding.d.ts.map +1 -1
  865. package/dist/registry/onboarding.js +22 -7
  866. package/dist/registry/onboarding.js.map +1 -1
  867. package/dist/registry/scanning.d.ts +7 -0
  868. package/dist/registry/scanning.d.ts.map +1 -0
  869. package/dist/registry/scanning.js +13 -0
  870. package/dist/registry/scanning.js.map +1 -0
  871. package/dist/registry/ship-stats-snapshot.d.ts.map +1 -1
  872. package/dist/registry/ship-stats-snapshot.js +38 -24
  873. package/dist/registry/ship-stats-snapshot.js.map +1 -1
  874. package/dist/registry/ships.d.ts +22 -2
  875. package/dist/registry/ships.d.ts.map +1 -1
  876. package/dist/registry/ships.js +31 -4
  877. package/dist/registry/ships.js.map +1 -1
  878. package/dist/registry/xp-runtime.d.ts +4 -0
  879. package/dist/registry/xp-runtime.d.ts.map +1 -0
  880. package/dist/registry/xp-runtime.js +14 -0
  881. package/dist/registry/xp-runtime.js.map +1 -0
  882. package/dist/rewards/index.d.ts +11 -5
  883. package/dist/rewards/index.d.ts.map +1 -1
  884. package/dist/rewards/index.js +10 -21
  885. package/dist/rewards/index.js.map +1 -1
  886. package/dist/scanning/actions/index.d.ts +116 -0
  887. package/dist/scanning/actions/index.d.ts.map +1 -0
  888. package/dist/scanning/actions/index.js +215 -0
  889. package/dist/scanning/actions/index.js.map +1 -0
  890. package/dist/scanning/index.d.ts +10 -12
  891. package/dist/scanning/index.d.ts.map +1 -1
  892. package/dist/scanning/index.js +19 -23
  893. package/dist/scanning/index.js.map +1 -1
  894. package/dist/scanning/lifecycle.d.ts +77 -0
  895. package/dist/scanning/lifecycle.d.ts.map +1 -0
  896. package/dist/scanning/lifecycle.js +93 -0
  897. package/dist/scanning/lifecycle.js.map +1 -0
  898. package/dist/scanning/odds.d.ts +115 -0
  899. package/dist/scanning/odds.d.ts.map +1 -0
  900. package/dist/scanning/odds.js +294 -0
  901. package/dist/scanning/odds.js.map +1 -0
  902. package/dist/scanning/signals.d.ts +126 -0
  903. package/dist/scanning/signals.d.ts.map +1 -0
  904. package/dist/scanning/signals.js +173 -0
  905. package/dist/scanning/signals.js.map +1 -0
  906. package/dist/starbases/actions/index.d.ts +49 -4
  907. package/dist/starbases/actions/index.d.ts.map +1 -1
  908. package/dist/starbases/actions/index.js +120 -69
  909. package/dist/starbases/actions/index.js.map +1 -1
  910. package/dist/starbases/index.d.ts +18 -0
  911. package/dist/starbases/index.d.ts.map +1 -1
  912. package/dist/starbases/index.js +24 -11
  913. package/dist/starbases/index.js.map +1 -1
  914. package/dist/world/index.d.ts +185 -6
  915. package/dist/world/index.d.ts.map +1 -1
  916. package/dist/world/index.js +186 -15
  917. package/dist/world/index.js.map +1 -1
  918. package/dist/world/planet-claim-stake-plots.d.ts +139 -0
  919. package/dist/world/planet-claim-stake-plots.d.ts.map +1 -0
  920. package/dist/world/planet-claim-stake-plots.js +117 -0
  921. package/dist/world/planet-claim-stake-plots.js.map +1 -0
  922. package/dist/world/starbase-status.d.ts +217 -0
  923. package/dist/world/starbase-status.d.ts.map +1 -0
  924. package/dist/world/starbase-status.js +218 -0
  925. package/dist/world/starbase-status.js.map +1 -0
  926. package/package.json +6 -2
@@ -1,21 +1,49 @@
1
- import { getAddressEncoder, getBytesEncoder, getProgramDerivedAddress, isAddress, } from '@solana/kit';
1
+ import { deriveCurrencyCache, validateCurrencyCache, } from '../../internal/c4/currency-cache.js';
2
+ import { isAddress } from '@solana/kit';
2
3
  import { InvalidActionInputError, SageSdkError } from '../../client/errors.js';
3
4
  import { readContextRawAccounts } from '../../client/reconciliation.js';
4
- import { sage } from '../../internal/c4/bindings.js';
5
+ import { StarbaseLevel } from '../../internal/c4/runtime-bindings.js';
5
6
  import { readFreshGameClaimStakeConfiguration, } from '../../registry/claim-stakes.js';
6
- import { characterAccountDefinition, findCharacterPda, } from '../../internal/c4/identity.js';
7
+ import { characterAccountDefinition, findCharacterPda, } from '../../internal/c4/character.js';
7
8
  import { deriveFactionOwnershipAddress, factionOwnershipAccountDefinition, } from '../../internal/c4/factions.js';
8
9
  import { claimStakeAccountDefinition } from '../../internal/c4/claim-stake.js';
9
- import { deriveFleetCrewBindingAddress, fleetCrewBindingAccountDefinition, } from '../../internal/c4/fleet-crew-binding.js';
10
+ import { deriveClaimStakeRegistration } from '../../internal/c4/claim-stake-standing.js';
11
+ import { CLAIM_STAKE_PLOT_ROWS, claimStakePlacementRow, claimStakePlotPositions, } from '../../internal/c4/claim-stake-plots.js';
12
+ import { factionValue } from '../../internal/c4/faction.js';
13
+ import { deriveFleetCrewBindingAddress, fleetCrewBindingAccountDefinition, matchesFleetCrewBindingIdentity, } from '../../internal/c4/fleet-crew-binding.js';
10
14
  import { deriveFleetAddress, fleetAccountDefinition, } from '../../internal/c4/fleet.js';
11
15
  import { deriveProfileFactionAddress, profileFactionIdentityAccountDefinition, } from '../../internal/c4/profile-faction.js';
12
16
  import { deriveStarbasePlayerAddress, starbasePlayerAccountDefinition, } from '../../internal/c4/starbase.js';
13
- import { celestialBodyAccountDefinition, decodeIndexedCelestialBodyAccount, deriveCelestialBodyAddress, deriveStarSystemAddress, starSystemAccountDefinition, } from '../../internal/c4/world.js';
17
+ import { celestialBodyAccountDefinition, deriveCelestialBodyAddress, deriveStarSystemAddress, starSystemAccountDefinition, } from '../../internal/c4/world.js';
18
+ import { resolveXpRuntime } from '../../internal/planning/xp-runtime.js';
19
+ import { readClaimStakeStorageAction } from '../../internal/planning/claim-stake-storage-actions.js';
20
+ import { RENT_ADDRESS } from '../../internal/planning/rent.js';
21
+ import { planStorageFunding } from '../../internal/planning/storage-actions.js';
22
+ import { requireDisabledStakeRent, requireNewStakeRent, } from '../../internal/planning/rent-catalog.js';
23
+ import { deriveStructureXpAccrualAddress } from '../../internal/c4/pda.js';
24
+ import { requireProfileStarbasePlayer } from '../../internal/planning/starbase-player-owner.js';
14
25
  import { requireGameAddress } from '../../internal/game-context.js';
15
- import { adaptCancelClaimStakeBuildingChangesInstruction, adaptClaimStakeBuildingChangesInstruction, adaptDeconstructClaimStakeInstruction, adaptFinalizeClaimStakeBuildingChangesInstruction, adaptPlaceClaimStakeInstruction, adaptRecoverClaimStakeFleetTransferInstruction, adaptStartClaimStakeFleetTransferInstruction, adaptTopUpClaimStakeRentInstruction, validatePlanAuthorization, } from '../../internal/planning/authorization-adapter.js';
16
- import { createPlan, } from '../../planning/index.js';
26
+ import { cargoStorageUnits } from '../../internal/cargo.js';
27
+ import { adaptCancelClaimStakeBuildingChangesInstruction, adaptClaimStakeBuildingChangesInstruction, adaptDeconstructClaimStakeInstruction, adaptFinalizeClaimStakeBuildingChangesInstruction, adaptPlaceClaimStakeInstruction, adaptRecoverClaimStakeFleetTransferInstruction, adaptStartClaimStakeFleetTransferInstruction, adaptTopUpClaimStakeRentInstruction, readPlanAuthorization, } from '../../internal/planning/authorization-adapter.js';
28
+ import { buildPlannerPlan } from '../../planning/plan-construction.js';
17
29
  import { readFreshGameCargoDefinitions } from '../../registry/cargo.js';
18
30
  import { resolveCargo } from '../../registry/index.js';
31
+ import { accountAbsentPrecondition, accountStatePrecondition, } from '../../internal/planning/account-state.js';
32
+ async function resolveClaimStakeXp(ctx, game, structure, character) {
33
+ const xp = await resolveXpRuntime(ctx, {
34
+ game,
35
+ structure,
36
+ ...(character === undefined ? {} : { character }),
37
+ });
38
+ const guard = {
39
+ kind: 'xp-runtime',
40
+ address: game,
41
+ intent: xp.intent,
42
+ accounts: xp.accounts,
43
+ describes: 'The Game and canonical Claim Stake XP accounts remain supported; accrued XP may change. Initialize or repair missing XP state using the game client, then recreate the Plan.',
44
+ };
45
+ return { accounts: xp.accounts, guard };
46
+ }
19
47
  const U64_MAX = (1n << 64n) - 1n;
20
48
  const U16_MAX = 65_535;
21
49
  const I32_MAX = 2_147_483_647;
@@ -31,13 +59,13 @@ const LEVEL_INDEX = Object.freeze({
31
59
  css: 6,
32
60
  });
33
61
  const CLAIM_STAKE_PLOT_LEVELS = Object.freeze([
34
- sage.StarbaseLevel.Level0,
35
- sage.StarbaseLevel.Level1,
36
- sage.StarbaseLevel.Level2,
37
- sage.StarbaseLevel.Level3,
38
- sage.StarbaseLevel.Level4,
39
- sage.StarbaseLevel.Level5,
40
- sage.StarbaseLevel.Css,
62
+ StarbaseLevel.Level0,
63
+ StarbaseLevel.Level1,
64
+ StarbaseLevel.Level2,
65
+ StarbaseLevel.Level3,
66
+ StarbaseLevel.Level4,
67
+ StarbaseLevel.Level5,
68
+ StarbaseLevel.Css,
41
69
  ]);
42
70
  const TIER_INDEX = Object.freeze({
43
71
  one: 0,
@@ -83,51 +111,28 @@ function u16(value, field) {
83
111
  }
84
112
  function validateOptions(value) {
85
113
  const options = record(value, 'options');
86
- exactKeys(options, 'options', [
87
- 'authorization',
88
- 'claimStake',
89
- 'claimStakeDefinitionId',
90
- 'hubBuildingId',
91
- 'initialRentAmount',
92
- ]);
93
- const authorizationRecord = record(options.authorization, 'authorization');
94
- exactKeys(authorizationRecord, 'authorization', ['profile', 'authority', 'keyIndex'], ['certificate']);
95
- const authorization = authorizationRecord;
96
- address(authorization.profile, 'authorization.profile');
97
- address(authorization.authority, 'authorization.authority');
98
- if (authorization.certificate !== undefined) {
99
- address(authorization.certificate, 'authorization.certificate');
100
- }
101
- validatePlanAuthorization(authorization);
102
- if (typeof options.initialRentAmount !== 'bigint' ||
103
- options.initialRentAmount <= 0n ||
104
- options.initialRentAmount > U64_MAX) {
105
- invalid('initialRentAmount', options.initialRentAmount, `a positive raw bigint from 1 through ${String(U64_MAX)} (unsigned u64)`, 'Set initialRentAmount to the exact positive raw rent deposit within the instruction codec range.');
114
+ exactKeys(options, 'options', ['authorization', 'claimStake', 'claimStakeDefinitionId', 'hubBuildingId'], ['initialRentAmount']);
115
+ const authorization = readPlanAuthorization(options.authorization);
116
+ if (options.initialRentAmount !== undefined &&
117
+ (typeof options.initialRentAmount !== 'bigint' ||
118
+ options.initialRentAmount <= 0n ||
119
+ options.initialRentAmount > U64_MAX)) {
120
+ invalid('initialRentAmount', options.initialRentAmount, `a positive raw bigint from 1 through ${String(U64_MAX)} (unsigned u64)`, 'Set initialRentAmount to the exact positive raw rent deposit within the instruction codec range, or omit it to place without rent.');
106
121
  }
107
122
  return {
108
123
  authorization,
109
124
  claimStake: address(options.claimStake, 'claimStake'),
110
125
  claimStakeDefinitionId: u16(options.claimStakeDefinitionId, 'claimStakeDefinitionId'),
111
126
  hubBuildingId: u16(options.hubBuildingId, 'hubBuildingId'),
112
- initialRentAmount: options.initialRentAmount,
127
+ ...(options.initialRentAmount === undefined
128
+ ? {}
129
+ : { initialRentAmount: options.initialRentAmount }),
113
130
  };
114
131
  }
115
- function validateAuthorization(value) {
116
- const authorizationRecord = record(value, 'authorization');
117
- exactKeys(authorizationRecord, 'authorization', ['profile', 'authority', 'keyIndex'], ['certificate']);
118
- const authorization = authorizationRecord;
119
- address(authorization.profile, 'authorization.profile');
120
- address(authorization.authority, 'authorization.authority');
121
- if (authorization.certificate !== undefined) {
122
- address(authorization.certificate, 'authorization.certificate');
123
- }
124
- validatePlanAuthorization(authorization);
125
- return authorization;
126
- }
127
132
  function validateBuildingChangeOptions(value) {
128
133
  const options = record(value, 'options');
129
134
  exactKeys(options, 'options', ['authorization', 'buildingChanges']);
130
- const authorization = validateAuthorization(options.authorization);
135
+ const authorization = readPlanAuthorization(options.authorization);
131
136
  const values = options.buildingChanges;
132
137
  if (!Array.isArray(values) || values.length === 0) {
133
138
  invalid('buildingChanges', values, 'a non-empty dense array of add/remove building changes', 'Provide at least one exact building change before retrying.');
@@ -168,22 +173,22 @@ function validateBuildingChangeOptions(value) {
168
173
  function validateFinalizeBuildingChangeOptions(value) {
169
174
  const options = record(value, 'options');
170
175
  exactKeys(options, 'options', ['authorization']);
171
- return { authorization: validateAuthorization(options.authorization) };
176
+ return { authorization: readPlanAuthorization(options.authorization) };
172
177
  }
173
178
  function validateCancelBuildingChangeOptions(value) {
174
179
  const options = record(value, 'options');
175
180
  exactKeys(options, 'options', ['authorization']);
176
- return { authorization: validateAuthorization(options.authorization) };
181
+ return { authorization: readPlanAuthorization(options.authorization) };
177
182
  }
178
183
  function validateDeconstructOptions(value) {
179
184
  const options = record(value, 'options');
180
185
  exactKeys(options, 'options', ['authorization']);
181
- return { authorization: validateAuthorization(options.authorization) };
186
+ return { authorization: readPlanAuthorization(options.authorization) };
182
187
  }
183
188
  function validateTopUpRentOptions(value) {
184
189
  const options = record(value, 'options');
185
190
  exactKeys(options, 'options', ['amount', 'authorization']);
186
- const authorization = validateAuthorization(options.authorization);
191
+ const authorization = readPlanAuthorization(options.authorization);
187
192
  if (typeof options.amount !== 'bigint' ||
188
193
  options.amount <= 0n ||
189
194
  options.amount > U64_MAX) {
@@ -220,7 +225,7 @@ function validateCargoRequests(value, field, seen) {
220
225
  function validateStartFleetTransferOptions(value) {
221
226
  const options = record(value, 'options');
222
227
  exactKeys(options, 'options', ['authorization', 'load', 'unload']);
223
- const authorization = validateAuthorization(options.authorization);
228
+ const authorization = readPlanAuthorization(options.authorization);
224
229
  const seen = new Set();
225
230
  const load = validateCargoRequests(options.load, 'load', seen);
226
231
  const unload = validateCargoRequests(options.unload, 'unload', seen);
@@ -232,7 +237,7 @@ function validateStartFleetTransferOptions(value) {
232
237
  function validateRecoverFleetTransferOptions(value) {
233
238
  const options = record(value, 'options');
234
239
  exactKeys(options, 'options', ['atUnixSeconds', 'authorization']);
235
- const authorization = validateAuthorization(options.authorization);
240
+ const authorization = readPlanAuthorization(options.authorization);
236
241
  if (typeof options.atUnixSeconds !== 'bigint' ||
237
242
  options.atUnixSeconds < 0n ||
238
243
  options.atUnixSeconds > U64_MAX) {
@@ -240,53 +245,9 @@ function validateRecoverFleetTransferOptions(value) {
240
245
  }
241
246
  return { atUnixSeconds: options.atUnixSeconds, authorization };
242
247
  }
243
- async function deriveCurrencyCacheAddress(game) {
244
- const [derived] = await getProgramDerivedAddress({
245
- programAddress: sage.SAGE_PROGRAM_ADDRESS,
246
- seeds: [
247
- getBytesEncoder().encode(new TextEncoder().encode('CurrencyConfig')),
248
- getAddressEncoder().encode(game),
249
- ],
250
- });
251
- return derived;
252
- }
253
- function validateCurrencyCache(account, game, canonicalAddress, action) {
254
- const decoder = sage.getCurrencyConfigCacheDecoder();
255
- const validDiscriminator = sage.CURRENCY_CONFIG_CACHE_DISCRIMINATOR.every((byte, index) => account.data[index] === byte);
256
- if (account.address !== canonicalAddress ||
257
- account.owner !== sage.SAGE_PROGRAM_ADDRESS ||
258
- account.data.length !== decoder.fixedSize ||
259
- !validDiscriminator ||
260
- decoder.decode(account.data).gameId !== game) {
261
- fail(`CurrencyCache ${account.address} does not match the configured Game, canonical PDA, owner, or pinned account layout. Refresh the Game currency configuration and retry ${action}.`, account.address);
262
- }
263
- }
264
248
  function requireAccount(account, accountAddress, message) {
265
249
  return account ?? fail(message, accountAddress);
266
250
  }
267
- async function hashAccountState(account) {
268
- const owner = getAddressEncoder().encode(account.owner);
269
- const canonical = new Uint8Array(owner.length + account.data.length);
270
- canonical.set(owner);
271
- canonical.set(account.data, owner.length);
272
- const digest = new Uint8Array(await globalThis.crypto.subtle.digest('SHA-256', canonical));
273
- return [...digest].map((byte) => byte.toString(16).padStart(2, '0')).join('');
274
- }
275
- async function precondition(account, describes) {
276
- return Object.freeze({
277
- address: account.address,
278
- dataHash: await hashAccountState(account),
279
- describes,
280
- kind: 'account-state',
281
- });
282
- }
283
- function absentPrecondition(accountAddress, describes) {
284
- return Object.freeze({
285
- address: accountAddress,
286
- describes,
287
- kind: 'account-absent',
288
- });
289
- }
290
251
  function findDefinition(configuration, id, game) {
291
252
  const definition = configuration.claimStakes.definitions.find((candidate) => candidate.id === id);
292
253
  return (definition ??
@@ -297,18 +258,27 @@ function findHub(configuration, id, game) {
297
258
  return (hub ??
298
259
  fail(`Hub building ${String(id)} is not a member of the configured Game building catalog. Choose an exact current bundled hub id, refresh the Game definitions, and retry.`, game));
299
260
  }
300
- function claimedCount(details, definitionId) {
301
- return (details.claimedPlots.find(({ key }) => key === definitionId)?.structures
302
- .length ?? 0);
303
- }
304
261
  /** Plans placement of one Claim Stake with its exact bundled hub.
305
262
  *
306
263
  * The planner validates the configured Game/Profile/Character/System/Body
307
264
  * graph, current catalog membership, known plot/cargo/crew availability, and
308
- * absence of the caller-created Claim Stake account. It returns one inert Plan;
265
+ * absence of the caller-created Claim Stake account. It also checks the
266
+ * Character's productive plot count and tier limits, the bundled hub's tags
267
+ * against the Planet and Claim Stake definition, and that the Character's
268
+ * ATLAS covers the placement fee plus any initial rent. It returns one inert Plan;
309
269
  * permissions, placement races, rent arithmetic, and program capacity remain
310
270
  * chain-authoritative.
311
271
  *
272
+ * The new Claim Stake's rent settlement authority must not exist and stays
273
+ * guarded absent until signing.
274
+ *
275
+ * The program funds the new stake to its rent floor, the size it can reach
276
+ * once Deactivated with a cargo line for each resource it produces, and the
277
+ * Planet's claimed plot from the Profile vault with the signing key. The Plan
278
+ * bounds that debit; when it is positive the key must be Profile AUTH or hold
279
+ * `drainSolVault`, and the Profile needs that many lamports above its rent
280
+ * reserve. Execution checks again before signing.
281
+ *
312
282
  * @example
313
283
  * ```ts
314
284
  * declare const ctx: SageContext;
@@ -332,21 +302,11 @@ export async function planPlaceClaimStake(ctx, body, player, input) {
332
302
  if (canonicalBody !== body.address) {
333
303
  fail(`Celestial Body ${body.address} is not the canonical Body ${String(body.id)} for the configured Game. Refresh the Body and retry.`, body.address);
334
304
  }
335
- if (player.playerProfile.address !== options.authorization.profile) {
336
- fail(`Starbase Player ${player.address} does not belong to authorization Profile ${options.authorization.profile}. Refresh the Starbase Player for that Profile and retry.`, player.address);
337
- }
338
- const character = await findCharacterPda(options.authorization.profile, game);
339
- if (player.character.address !== character) {
340
- fail(`Starbase Player ${player.address} does not reference the canonical Character ${character} for authorization Profile ${options.authorization.profile}. Refresh the Starbase Player and retry.`, player.address);
341
- }
342
- const canonicalPlayer = await deriveStarbasePlayerAddress(player.system.address, character);
343
- if (canonicalPlayer !== player.address) {
344
- fail(`Starbase Player ${player.address} is not canonical for its Character and System. Refresh the Starbase Player and retry.`, player.address);
345
- }
305
+ const character = await requireProfileStarbasePlayer(player, options.authorization.profile, game);
346
306
  if (body.system.address !== player.system.address) {
347
307
  fail(`Celestial Body ${body.address} belongs to System ${body.system.address}, but Starbase Player ${player.address} belongs to System ${player.system.address}. Choose both snapshots from the same System and retry.`, body.address);
348
308
  }
349
- const [freshGame, currentAccounts] = await Promise.all([
309
+ const [freshGame, currentAccounts, rent] = await Promise.all([
350
310
  readFreshGameClaimStakeConfiguration(ctx),
351
311
  readContextRawAccounts(ctx, [
352
312
  body.address,
@@ -355,13 +315,15 @@ export async function planPlaceClaimStake(ctx, body, player, input) {
355
315
  body.system.address,
356
316
  options.claimStake,
357
317
  ]),
318
+ requireNewStakeRent(ctx, options.claimStake),
358
319
  ]);
359
320
  if (freshGame === undefined) {
360
321
  fail(`Configured Game ${game} is missing. Refresh the Game catalog and retry placement.`, game);
361
322
  }
362
323
  const definition = findDefinition(freshGame.data, options.claimStakeDefinitionId, game);
363
324
  const hub = findHub(freshGame.data, options.hubBuildingId, game);
364
- if (hub.hubValue !== definition.hubValue ||
325
+ if (hub.hubValue <= 0 ||
326
+ hub.hubValue !== definition.hubValue ||
365
327
  hub.minimumTier !== definition.tier ||
366
328
  hub.constructionCost.length !== 0) {
367
329
  fail(`Hub building ${hub.id} (${hub.name}) is not the exact zero-cost ${definition.tier} bundled hub for Claim Stake definition ${definition.id} (${definition.name}). Choose the matching bundled hub from the current Game catalog and retry.`, game);
@@ -385,11 +347,10 @@ export async function planPlaceClaimStake(ctx, body, player, input) {
385
347
  }
386
348
  const currentBodyAccount = requireAccount(bodyAccount, body.address, `Celestial Body ${body.address} is missing. Refresh the Body and retry placement.`);
387
349
  const currentPlayerAccount = requireAccount(playerAccount, player.address, `Starbase Player ${player.address} is missing. Refresh it and retry placement.`);
388
- const currentCharacterAccount = requireAccount(characterAccount, character, `Canonical Character ${character} is missing. Refresh the Profile Character and retry placement.`);
389
350
  const currentSystemAccount = requireAccount(systemAccount, body.system.address, `Star System ${body.system.address} is missing. Refresh the System and retry placement.`);
390
351
  const currentBody = celestialBodyAccountDefinition.decode(currentBodyAccount);
391
352
  const currentPlayer = starbasePlayerAccountDefinition.decode(currentPlayerAccount);
392
- const currentCharacter = characterAccountDefinition.decode(currentCharacterAccount);
353
+ const { account: currentCharacterAccount, current: currentCharacter } = requireProfileCharacter(characterAccount, character, options.authorization.profile, game, 'placement');
393
354
  const currentSystem = starSystemAccountDefinition.decode(currentSystemAccount);
394
355
  const canonicalSystem = await deriveStarSystemAddress(game, currentSystem.systemId);
395
356
  if (canonicalSystem !== body.system.address ||
@@ -411,10 +372,6 @@ export async function planPlaceClaimStake(ctx, body, player, input) {
411
372
  currentPlayer.totalCrew - currentPlayer.busyCrew !== player.availableCrew) {
412
373
  fail(`Starbase Player ${player.address} changed or does not match the authorization Profile, canonical Character, System, or available crew. Refresh it and retry.`, player.address);
413
374
  }
414
- if (currentCharacter.playerProfile !== options.authorization.profile ||
415
- currentCharacter.game !== game) {
416
- fail(`Character ${character} does not match authorization Profile ${options.authorization.profile} and configured Game ${game}. Refresh the Character and retry.`, character);
417
- }
418
375
  const currentCargo = currentPlayer.cargo.amounts.find(({ cargoId }) => cargoId === definition.cargoId)?.amount ?? 0n;
419
376
  if (currentCargo !== cargoQuantity || currentCargo < 1n) {
420
377
  fail(`Starbase Player ${player.address} Claim Stake cargo changed from the loaded snapshot or is no longer sufficient. Refresh its cargo and retry.`, player.address);
@@ -424,51 +381,100 @@ export async function planPlaceClaimStake(ctx, body, player, input) {
424
381
  if (currentMissingTag !== undefined) {
425
382
  fail(`Planet ${body.name} (${body.address}) no longer provides required Claim Stake tag ${String(currentMissingTag)} for ${definition.name}. Refresh it and retry.`, body.address);
426
383
  }
384
+ // The bundled hub's tags must come from the Planet and the Claim Stake
385
+ // definition; the hub cannot satisfy its own requirements.
386
+ const hubMissingTag = hub.requiredTagIds.find((tag) => !currentDetails.providedTags.includes(tag) &&
387
+ !definition.providedTagIds.includes(tag));
388
+ if (hubMissingTag !== undefined) {
389
+ fail(`Bundled hub ${hub.name} requires tag ${String(hubMissingTag)}, which neither Planet ${body.name} nor ${definition.name} provides. Choose another Planet or definition.`, body.address);
390
+ }
427
391
  const level = LEVEL_INDEX[currentSystem.starbase.level];
428
392
  const tier = TIER_INDEX[definition.tier];
429
- const plotLimit = currentDetails.claimStakePlots[level][tier];
430
- const usedPlots = claimedCount(currentDetails, definition.id);
431
- if (usedPlots >= plotLimit) {
432
- fail(`Planet ${body.name} (${body.address}) has no available ${definition.name} plot at Starbase ${currentSystem.starbase.level}; ${String(usedPlots)} of ${String(plotLimit)} known plots are occupied. Choose another eligible Planet or tier, refresh it, and retry.`, body.address);
433
- }
393
+ // The program places the stake in the first plot row up to the Starbase's
394
+ // level with room for its tier; stakes of every tier share each row.
395
+ if (claimStakePlacementRow(currentDetails.claimStakePlots, currentDetails.claimedPlots, tier, level) === undefined) {
396
+ fail(`Planet ${body.name} (${body.address}) has no free tier-${definition.tier} Claim Stake plot at Starbase ${currentSystem.starbase.level} or any lower level; every reachable plot row is full. Choose another eligible Planet or tier, refresh it, and retry.`, body.address);
397
+ }
398
+ // The Character's research caps the productive plot tier and count.
399
+ const modifiers = currentCharacter.modifiers.values;
400
+ if (modifiers.maxProductivePlotTier !== undefined &&
401
+ tier + 1 > modifiers.maxProductivePlotTier)
402
+ fail(`Character ${character} may place up to tier ${String(modifiers.maxProductivePlotTier)}, below ${definition.name}'s tier ${definition.tier}. Research a higher plot tier or pick a lower tier.`, character);
403
+ if (currentCharacter.counts.claimStakes >= modifiers.maxProductivePlots)
404
+ fail(`Character ${character} already holds ${String(currentCharacter.counts.claimStakes)} of its ${String(modifiers.maxProductivePlots)} productive plots. Research more plots or deconstruct a Claim Stake.`, character);
405
+ const placementFee = claimStakePlacementFee(currentDetails.baseClaimStakePlacementFee[level][tier].raw, definition.placementFeeMultiplier.raw);
406
+ const rentDeposit = options.initialRentAmount ?? 0n;
407
+ if (currentCharacter.atlasRaw < placementFee + rentDeposit)
408
+ fail(`Character ${character} has ${String(currentCharacter.atlasRaw)} raw ATLAS, below the ${String(placementFee)} raw placement fee plus ${String(rentDeposit)} raw initial rent. Add ATLAS or lower the rent, then retry.`, character);
434
409
  const profileFaction = await deriveProfileFactionAddress(options.authorization.profile);
435
- const currencyCache = await deriveCurrencyCacheAddress(game);
436
- const description = `Place ${definition.name} on Planet ${body.name} (${body.address}) with bundled hub ${hub.name}, ${String(hub.neededCrew)} crew, and ${String(options.initialRentAmount)} raw initial rent.`;
437
- return createPlan({
410
+ const currencyCache = await deriveCurrencyCache(game).then(([address]) => address);
411
+ const instruction = adaptPlaceClaimStakeInstruction({
412
+ authorization: options.authorization,
413
+ celestialBody: body.address,
414
+ character,
415
+ claimStakeDefinitionId: definition.id,
416
+ claimStakeInstance: options.claimStake,
417
+ currencyCache,
418
+ game,
419
+ hubBuildingId: hub.id,
420
+ initialRentAmount: options.initialRentAmount ?? null,
421
+ profileFaction,
422
+ rentAuthority: rent.rentAuthority,
423
+ systemAndStarbasePlayerStarbasePlayer: player.address,
424
+ systemAndStarbasePlayerSystem: body.system.address,
425
+ });
426
+ // The new stake and the claimed plot are funded from the Profile vault.
427
+ const storage = await planClaimStakeStorage(ctx, instruction, options.authorization.profile, [freshGame.account, currentBodyAccount, currentSystemAccount], 'Claim Stake placement');
428
+ const description = `Place ${definition.name} on Planet ${body.name} (${body.address}) with bundled hub ${hub.name}, ${String(hub.neededCrew)} crew, a ${String(placementFee)} raw ATLAS placement fee and ${String(rentDeposit)} raw initial rent. ${storage.disclosure}`;
429
+ return buildPlannerPlan({
438
430
  kind: 'claim-stakes.place',
439
431
  preconditions: [
440
432
  ...(await Promise.all([
441
- precondition(freshGame.account, `The configured Game retains Claim Stake definition ${String(definition.id)} and bundled hub ${String(hub.id)} eligibility.`),
442
- precondition(currentBodyAccount, `Planet ${body.name} retains an available ${definition.name} plot and its configured Game/System relationship.`),
443
- precondition(currentPlayerAccount, `Starbase Player ${player.address} retains its Profile, Character, System, available crew, and ${cargoName} balance.`),
444
- precondition(currentCharacterAccount, `The canonical Character remains assigned to Profile ${options.authorization.profile} in the configured Game.`),
445
- precondition(currentSystemAccount, `Star System ${currentSystem.name} retains its configured Game, sequence, and active Starbase level.`),
433
+ accountStatePrecondition(freshGame.account, `The configured Game retains Claim Stake definition ${String(definition.id)} and bundled hub ${String(hub.id)} eligibility.`),
434
+ accountStatePrecondition(currentBodyAccount, `Planet ${body.name} retains an available ${definition.name} plot and its configured Game/System relationship.`),
435
+ accountStatePrecondition(currentPlayerAccount, `Starbase Player ${player.address} retains its Profile, Character, System, available crew, and ${cargoName} balance.`),
436
+ characterPrecondition(currentCharacterAccount, options.authorization.profile),
437
+ accountStatePrecondition(currentSystemAccount, `Star System ${currentSystem.name} retains its configured Game, sequence, and active Starbase level.`),
446
438
  ])),
447
- absentPrecondition(options.claimStake, 'The caller-supplied Claim Stake signer address remains unallocated.'),
439
+ accountAbsentPrecondition(options.claimStake, 'The caller-supplied Claim Stake signer address remains unallocated.'),
440
+ ...rent.guards,
441
+ storage.guard,
448
442
  ],
449
443
  steps: [
450
444
  {
451
445
  describes: description,
452
- instruction: adaptPlaceClaimStakeInstruction({
453
- authorization: options.authorization,
454
- celestialBody: body.address,
455
- character,
456
- claimStakeDefinitionId: definition.id,
457
- claimStakeInstance: options.claimStake,
458
- currencyCache,
459
- game,
460
- hubBuildingId: hub.id,
461
- initialRentAmount: options.initialRentAmount,
462
- profileFaction,
463
- systemAndStarbasePlayerStarbasePlayer: player.address,
464
- systemAndStarbasePlayerSystem: body.system.address,
465
- }),
446
+ instruction,
466
447
  signers: [options.authorization.authority, options.claimStake],
467
448
  },
468
449
  ],
469
450
  summary: description,
470
451
  });
471
452
  }
453
+ /** Plans a Claim Stake action's Profile storage funding from the fresh
454
+ * accounts the planner already read, plus the Profile and Rent sysvar. */
455
+ async function planClaimStakeStorage(ctx, instruction, profile, known, label) {
456
+ const [profileAccount, rentAccount] = await readContextRawAccounts(ctx, [
457
+ profile,
458
+ RENT_ADDRESS,
459
+ ]);
460
+ const accounts = new Map([
461
+ ...known.map((raw) => [raw.address, raw]),
462
+ [profile, profileAccount],
463
+ [RENT_ADDRESS, rentAccount],
464
+ ]);
465
+ return planStorageFunding({
466
+ action: readClaimStakeStorageAction(instruction, accounts),
467
+ accounts,
468
+ label,
469
+ });
470
+ }
471
+ /** The ATLAS placement fee: the Planet's U32F32 base fee for the Starbase
472
+ * level and tier, times the definition's U8F56 multiplier, rounded to the
473
+ * nearest raw unit with halves rounded up. */
474
+ function claimStakePlacementFee(baseFeeRaw, multiplierRaw) {
475
+ const product = ((baseFeeRaw << 24n) * multiplierRaw) >> 56n;
476
+ return (product + (1n << 55n)) >> 56n;
477
+ }
472
478
  function quantities(buildings) {
473
479
  return new Map(buildings.map(({ buildingId, quantity }) => [buildingId, quantity]));
474
480
  }
@@ -481,9 +487,10 @@ function sameQuantities(left, right) {
481
487
  [...leftById].every(([buildingId, quantity]) => rightById.get(buildingId) === quantity));
482
488
  }
483
489
  function sameStakeState(left, right) {
484
- if (left.kind === 'deactivated')
485
- return false;
486
490
  switch (left.kind) {
491
+ case 'deactivated':
492
+ return (right.kind === 'deactivated' &&
493
+ left.deactivatedAtUnixSeconds === right.deactivatedAtUnixSeconds);
487
494
  case 'active':
488
495
  return right.kind === 'active';
489
496
  case 'design':
@@ -492,12 +499,192 @@ function sameStakeState(left, right) {
492
499
  sameQuantities(left.buildings, right.buildings));
493
500
  }
494
501
  }
495
- const LEGACY_CONTROLLER_ID = Object.freeze({
496
- unaligned: 0,
497
- mud: 1,
498
- oni: 2,
499
- ustur: 3,
500
- });
502
+ /** Finalization and deconstruction release crew the stake committed, which
503
+ * only a version-1 instance records; the program refuses a version-0 instance,
504
+ * whose stored crew is an unverified nominal value. */
505
+ function requireCommittedCrewVersion(current, stake, outcome) {
506
+ if (current.version !== 1) {
507
+ fail(`Claim Stake ${stake.address} is a version ${String(current.version)} instance, which records no committed crew, so it cannot be ${outcome}. Only version-1 Claim Stakes support this action; choose another Claim Stake.`, stake.address);
508
+ }
509
+ }
510
+ // Common facts for planners acting on an owned, placed Claim Stake. Each
511
+ // planner keeps its own eligibility checks and Plan wording at its call site;
512
+ // `action` completes the shared remedies ("retry <action>").
513
+ /** Requires the loaded Claim Stake to belong to the configured Game and to the
514
+ * authorization Profile, and returns that Profile's canonical Character. */
515
+ async function requireOwnedStake(stake, game, profile) {
516
+ if (stake.game !== game) {
517
+ fail(`Claim Stake ${stake.address} does not match the configured Game ${game}. Refresh it through this context and retry.`, stake.address);
518
+ }
519
+ if (stake.ownerProfile.address !== profile) {
520
+ fail(`Claim Stake ${stake.address} belongs to Profile ${stake.ownerProfile.address}, not authorization Profile ${profile}. Choose a Claim Stake this Profile owns, refresh it, and retry.`, stake.address);
521
+ }
522
+ return findCharacterPda(profile, game);
523
+ }
524
+ /** Reads and decodes the fresh Claim Stake and Body, with `extra` accounts in
525
+ * the same batch. */
526
+ async function readOwnedStake(ctx, stake, action, extra = []) {
527
+ const [stakeCandidate, bodyCandidate, ...extraAccounts] = await readContextRawAccounts(ctx, [
528
+ stake.address,
529
+ stake.body.address,
530
+ ...extra,
531
+ ]);
532
+ const stakeAccount = requireAccount(stakeCandidate, stake.address, `Claim Stake ${stake.address} is missing. Refresh it and retry ${action}.`);
533
+ const bodyAccount = requireAccount(bodyCandidate, stake.body.address, `Celestial Body ${stake.body.address} is missing. Refresh the Claim Stake Body and retry ${action}.`);
534
+ return {
535
+ bodyAccount,
536
+ currentBody: celestialBodyAccountDefinition.decode(bodyAccount),
537
+ currentStake: claimStakeAccountDefinition.decode(stakeAccount),
538
+ extraAccounts,
539
+ stakeAccount,
540
+ };
541
+ }
542
+ /** Whether the fresh Claim Stake keeps the loaded ownership, Body,
543
+ * definitions, buildings and System generation. */
544
+ function sameOwnedStake(current, stake, game, profile) {
545
+ return (current.game === game &&
546
+ current.body === stake.body.address &&
547
+ current.ownerProfile === profile &&
548
+ current.claimStakeDefinitionId === stake.claimStakeDefinitionId &&
549
+ current.claimStakeDefinitionSeqId === stake.claimStakeDefinitionSeqId &&
550
+ current.buildingDefinitionSeqId === stake.buildingDefinitionSeqId &&
551
+ current.systemSeqId === stake.systemSeqId &&
552
+ sameQuantities(current.buildings, stake.buildings));
553
+ }
554
+ /** Whether the fresh Claim Stake keeps the loaded transfer facts: ownership,
555
+ * Body, active state, System generation, resources and capacity. */
556
+ function sameTransferStake(current, stake, game, profile) {
557
+ return (current.game === game &&
558
+ current.ownerProfile === profile &&
559
+ current.body === stake.body.address &&
560
+ current.systemSeqId === stake.systemSeqId &&
561
+ current.state.kind === 'active' &&
562
+ current.production.cargoPod.storageCost ===
563
+ stake.resources.capacity.usedRaw &&
564
+ current.production.resourceCapacityRaw ===
565
+ stake.resources.capacity.totalRaw &&
566
+ sameCargoAmounts(current.production.cargoPod.amounts, stake.resources.inventory.items.map(({ id, quantityRaw }) => ({
567
+ amount: quantityRaw,
568
+ id,
569
+ }))));
570
+ }
571
+ /** Whether the fresh Fleet keeps the loaded Game, owner, name, cargo and
572
+ * capacities. */
573
+ function sameTransferFleet(current, fleet, game, profile) {
574
+ return (current.game === game &&
575
+ current.ownerProfile === profile &&
576
+ current.name === fleet.name &&
577
+ current.ammoBank.cargoId === fleet.ammo.id &&
578
+ current.ammoBank.amount === fleet.ammo.amount &&
579
+ current.fuelTank.cargoId === fleet.fuel.id &&
580
+ current.fuelTank.amount === fleet.fuel.amount &&
581
+ current.cargoHold.storageCost === fleet.cargoHold.storageCost &&
582
+ sameCargoAmounts(current.cargoHold.amounts, fleet.cargoHold.items) &&
583
+ current.stats.cargo.ammoCapacity === fleet.capacities.ammo.total &&
584
+ current.stats.cargo.fuelCapacity === fleet.capacities.fuel.total &&
585
+ current.stats.cargo.cargoCapacity === fleet.capacities.cargo.total);
586
+ }
587
+ /** Requires the fresh Body to be the Claim Stake's canonical Planet in the
588
+ * configured Game at the Claim Stake's System generation. */
589
+ async function requireStakePlanet(currentBody, stake, game, action) {
590
+ if (currentBody.kind !== 'planet' ||
591
+ currentBody.game !== game ||
592
+ currentBody.systemSequenceId !== stake.systemSeqId ||
593
+ (await deriveCelestialBodyAddress(game, currentBody.id)) !==
594
+ stake.body.address) {
595
+ fail(`Celestial Body ${stake.body.address} does not match the Claim Stake's configured Game, canonical Planet identity, or System generation. Refresh the Body and Claim Stake before retrying ${action}.`, stake.body.address);
596
+ }
597
+ return currentBody;
598
+ }
599
+ /** Refuses a building change the game would refuse for the stake's Planet
600
+ * registration, which is independent of its rent. */
601
+ function requireStakeRegistered(claimedPlots, stake, starbaseLevel, action) {
602
+ const registration = deriveClaimStakeRegistration(stake, claimedPlots, starbaseLevel);
603
+ if (registration.kind === 'operable')
604
+ return;
605
+ fail(registration.kind === 'unregistered'
606
+ ? `Planet ${stake.body.address} does not register Claim Stake ${stake.address}. Refresh the Body and Claim Stake before retrying ${action}.`
607
+ : registration.kind === 'off-level'
608
+ ? `Claim Stake ${stake.address} is registered in plot row ${String(registration.plotRow)}, above the Starbase's current ${starbaseLevel}, so it is evicted and ${action} is refused. Respawn it to reclaim it.`
609
+ : `A Starbase takeover, destruction or downgrade evicted Claim Stake ${stake.address}, so ${action} is refused. Respawn it to reclaim it.`, stake.address);
610
+ }
611
+ /** {@link requireStakePlanet} for building changes. An eviction is reported
612
+ * first: a takeover or destruction also advances the System generation, which
613
+ * would otherwise hide the real cause behind a refresh remedy. */
614
+ async function requireDesignStakePlanet(currentBody, stake, game, action) {
615
+ if (currentBody.kind === 'planet')
616
+ requireStakeRegistered(currentBody.details.claimedPlots, stake, 'css', action);
617
+ return requireStakePlanet(currentBody, stake, game, action);
618
+ }
619
+ /** Requires and decodes the canonical Character of the authorization Profile in
620
+ * the configured Game. */
621
+ function requireProfileCharacter(candidate, character, profile, game, action) {
622
+ const account = requireAccount(candidate, character, `Canonical Character ${character} is missing. Refresh the Profile Character and retry ${action}.`);
623
+ const current = characterAccountDefinition.decode(account);
624
+ if (current.playerProfile !== profile || current.game !== game) {
625
+ fail(`Character ${character} does not match authorization Profile ${profile} and configured Game ${game}. Refresh the Character and retry ${action}.`, character);
626
+ }
627
+ return { account, current };
628
+ }
629
+ /** Reads the Claim Stake's System and the authorization Profile's canonical
630
+ * Starbase Player there, with `extra` accounts in the same batch. Requires a
631
+ * canonical, current System with an active Starbase that lists the Body, and a
632
+ * Starbase Player of the Profile, Character and System. */
633
+ async function readStakeStarbase(ctx, stake, game, system, character, profile, action, extra = []) {
634
+ const player = await deriveStarbasePlayerAddress(system, character);
635
+ const [systemCandidate, playerCandidate, ...extraAccounts] = await readContextRawAccounts(ctx, [system, player, ...extra]);
636
+ const systemAccount = requireAccount(systemCandidate, system, `Star System ${system} is missing. Refresh the Claim Stake's System and retry ${action}.`);
637
+ const playerAccount = requireAccount(playerCandidate, player, `Starbase Player ${player} is missing for authorization Profile ${profile}. Register or refresh it in the Claim Stake's System before retrying ${action}.`);
638
+ const currentSystem = starSystemAccountDefinition.decode(systemAccount);
639
+ const currentPlayer = starbasePlayerAccountDefinition.decode(playerAccount);
640
+ if (currentSystem.game !== game ||
641
+ currentSystem.sequenceId !== stake.systemSeqId ||
642
+ currentSystem.starbase === undefined ||
643
+ !currentSystem.celestialBodyAddresses.includes(stake.body.address) ||
644
+ (await deriveStarSystemAddress(game, currentSystem.systemId)) !== system) {
645
+ fail(`Star System ${system} does not match the configured Game, canonical identity, Claim Stake generation, active nested Starbase, or Claim Stake Body relationship. Refresh the System and retry ${action}.`, system);
646
+ }
647
+ if (currentPlayer.playerProfile !== profile ||
648
+ currentPlayer.character !== character ||
649
+ currentPlayer.system !== system) {
650
+ fail(`Starbase Player ${player} does not match the authorization Profile, canonical Character, and Claim Stake System. Refresh it and retry ${action}.`, player);
651
+ }
652
+ return {
653
+ currentPlayer,
654
+ currentSystem,
655
+ extraAccounts,
656
+ player,
657
+ playerAccount,
658
+ starbase: currentSystem.starbase,
659
+ systemAccount,
660
+ };
661
+ }
662
+ async function claimStakeCurrencyCache(game) {
663
+ const [currencyCache] = await deriveCurrencyCache(game);
664
+ return currencyCache;
665
+ }
666
+ /** Requires the canonical CurrencyCache and returns its Plan guard. */
667
+ async function requireCurrencyCache(candidate, currencyCache, game, action, planName) {
668
+ await validateCurrencyCache(requireAccount(candidate, currencyCache, `Canonical CurrencyCache ${currencyCache} is missing for configured Game ${game}. Refresh the Game currency configuration and retry ${action}.`), game, planName);
669
+ return {
670
+ kind: 'currency-cache',
671
+ address: currencyCache,
672
+ game,
673
+ describes: 'The canonical CurrencyCache remains assigned to the configured Game; shared accounting may change.',
674
+ };
675
+ }
676
+ function planetPrecondition(account, system) {
677
+ return accountStatePrecondition(account, `The Claim Stake Planet remains assigned to System ${system} at the current generation.`);
678
+ }
679
+ function characterPrecondition(account, profile) {
680
+ return accountStatePrecondition(account, `The canonical Character remains assigned to Profile ${profile} in the configured Game.`);
681
+ }
682
+ function systemPrecondition(account, name) {
683
+ return accountStatePrecondition(account, `Star System ${name} retains its configured Game, sequence, nested Starbase, and Claim Stake Body relationship.`);
684
+ }
685
+ function playerPrecondition(account, player, facts = 'Profile, Character, and System') {
686
+ return accountStatePrecondition(account, `Starbase Player ${player} retains its ${facts}.`);
687
+ }
501
688
  function buildingDescription(change, name) {
502
689
  return `${change.kind} ${String(change.quantity)} ${name}`;
503
690
  }
@@ -505,9 +692,26 @@ function buildingDescription(change, name) {
505
692
  *
506
693
  * The planner verifies the fresh Claim Stake, definition sequences, canonical
507
694
  * Profile/Character/Body/System/Starbase Player graph, optional
508
- * FactionOwnership sidecar, building quantities, slots, crew, and known
509
- * construction cargo. Rent synchronization and the final concurrent sequence
510
- * race remain chain-authoritative.
695
+ * FactionOwnership sidecar, building quantities, slots, Starbase Player crew,
696
+ * non-negative power, a single unremovable hub of quantity one, building tags
697
+ * provided by the Claim Stake definition, the Planet and the buildings, and
698
+ * known construction cargo. A stake whose stored construction time is still
699
+ * running is refused; that time advances only on a production tick. Construction cargo is debited from the Starbase Player's
700
+ * Starbase cargo at finalization, never from the Claim Stake's own storage, so
701
+ * the planner checks the fresh Starbase Player pod. Rent synchronization and
702
+ * the final concurrent sequence race remain chain-authoritative.
703
+ *
704
+ * Entering a design adds the design state and an entry for each building id
705
+ * the design does not yet hold. The program funds that growth, up to the
706
+ * stake's rent floor, from the Profile vault with the signing key; the Plan
707
+ * bounds the debit with the same key and balance checks as placement.
708
+ *
709
+ * Requires Game 2 and its existing canonical Claim Stake XP ledger.
710
+ * Missing XP state must be repaired outside this Plan; no accounts are created.
711
+ *
712
+ * Requires Disabled rent: the Game's rent catalog and the Claim Stake's rent
713
+ * settlement authority must not exist, and both stay guarded absent until
714
+ * signing. The instruction carries no rent history.
511
715
  *
512
716
  * @example
513
717
  * ```ts
@@ -521,88 +725,46 @@ function buildingDescription(change, name) {
521
725
  export async function planClaimStakeBuildingChanges(ctx, stake, input) {
522
726
  const options = validateBuildingChangeOptions(input);
523
727
  const game = requireGameAddress(ctx);
728
+ const { profile } = options.authorization;
524
729
  if (stake.state.kind === 'deactivated') {
525
730
  fail(`Claim Stake ${stake.address} is deactivated; building changes require an active or design Claim Stake. Refresh it or choose another owned Claim Stake before retrying.`, stake.address);
526
731
  }
527
- if (stake.game !== game) {
528
- fail(`Claim Stake ${stake.address} does not match the configured Game ${game}. Refresh it through this context and retry.`, stake.address);
529
- }
530
- const character = await findCharacterPda(options.authorization.profile, game);
531
- if (stake.ownerCharacter.address !== character) {
532
- fail(`Claim Stake ${stake.address} does not belong to authorization Profile ${options.authorization.profile}'s canonical Character ${character}. Choose an owned Claim Stake, refresh it, and retry.`, stake.address);
533
- }
534
- const [freshGame, firstAccounts] = await Promise.all([
732
+ const character = await requireOwnedStake(stake, game, profile);
733
+ const action = 'building changes';
734
+ const [freshGame, loaded, rent] = await Promise.all([
535
735
  readFreshGameClaimStakeConfiguration(ctx),
536
- readContextRawAccounts(ctx, [stake.address, stake.body.address, character]),
736
+ readOwnedStake(ctx, stake, action, [character]),
737
+ requireDisabledStakeRent(ctx, game, stake.address, action),
537
738
  ]);
538
739
  if (freshGame === undefined) {
539
740
  fail(`Configured Game ${game} is missing. Refresh the Game catalog and retry building changes.`, game);
540
741
  }
541
- const stakeAccount = requireAccount(firstAccounts[0], stake.address, `Claim Stake ${stake.address} is missing. Refresh it and retry building changes.`);
542
- const bodyAccount = requireAccount(firstAccounts[1], stake.body.address, `Celestial Body ${stake.body.address} is missing. Refresh the Claim Stake Body and retry building changes.`);
543
- const characterAccount = requireAccount(firstAccounts[2], character, `Canonical Character ${character} is missing. Refresh the Profile Character and retry building changes.`);
544
- const currentStake = claimStakeAccountDefinition.decode(stakeAccount);
545
- const currentBody = celestialBodyAccountDefinition.decode(bodyAccount);
546
- const currentCharacter = characterAccountDefinition.decode(characterAccount);
547
- if (currentStake.game !== game ||
548
- currentStake.body !== stake.body.address ||
549
- currentStake.ownerCharacter !== character ||
550
- currentStake.claimStakeDefinitionId !== stake.claimStakeDefinitionId ||
551
- currentStake.claimStakeDefinitionSeqId !==
552
- stake.claimStakeDefinitionSeqId ||
553
- currentStake.buildingDefinitionSeqId !== stake.buildingDefinitionSeqId ||
554
- currentStake.systemSeqId !== stake.systemSeqId ||
555
- !sameQuantities(currentStake.buildings, stake.buildings) ||
742
+ const { bodyAccount, currentStake, stakeAccount } = loaded;
743
+ if (!sameOwnedStake(currentStake, stake, game, profile) ||
556
744
  currentStake.neededCrew !== stake.neededCrew) {
557
745
  fail(`Claim Stake ${stake.address} changed or does not match its loaded ownership, Body, definitions, buildings, crew, or System generation. Refresh it and retry building changes.`, stake.address);
558
746
  }
559
- if (currentCharacter.playerProfile !== options.authorization.profile ||
560
- currentCharacter.game !== game) {
561
- fail(`Character ${character} does not match authorization Profile ${options.authorization.profile} and configured Game ${game}. Refresh the Character and retry building changes.`, character);
747
+ if (currentStake.constructionRemainingSeconds > 0n) {
748
+ fail(`Claim Stake ${stake.address} records ${String(currentStake.constructionRemainingSeconds)} seconds of construction remaining, and a stake under construction cannot change its buildings. The stored time advances only when a production tick processes the stake: wait for construction to finish, let a production tick record it, refresh the Claim Stake, and retry.`, stake.address);
562
749
  }
563
- if (currentBody.kind !== 'planet' ||
564
- currentBody.game !== game ||
565
- currentBody.systemSequenceId !== stake.systemSeqId ||
566
- (await deriveCelestialBodyAddress(game, currentBody.id)) !==
567
- stake.body.address) {
568
- fail(`Celestial Body ${stake.body.address} does not match the Claim Stake's configured Game, canonical Planet identity, or System generation. Refresh the Body and Claim Stake before retrying.`, stake.body.address);
569
- }
570
- const system = currentBody.system;
571
- const [player, ownership, profileFaction] = await Promise.all([
572
- deriveStarbasePlayerAddress(system, character),
750
+ const planetBody = await requireDesignStakePlanet(loaded.currentBody, stake, game, action);
751
+ const { system } = planetBody;
752
+ const { account: characterAccount } = requireProfileCharacter(loaded.extraAccounts[0], character, profile, game, action);
753
+ const [ownership, profileFaction] = await Promise.all([
573
754
  deriveFactionOwnershipAddress(game, 'starbase', system),
574
- deriveProfileFactionAddress(options.authorization.profile),
755
+ deriveProfileFactionAddress(profile),
575
756
  ]);
576
- const [systemAccountCandidate, playerAccountCandidate, ownershipAccount, profileFactionAccountCandidate,] = await readContextRawAccounts(ctx, [
577
- system,
578
- player,
579
- ownership,
580
- profileFaction,
581
- ]);
582
- const systemAccount = requireAccount(systemAccountCandidate, system, `Star System ${system} is missing. Refresh the Claim Stake's System and retry building changes.`);
583
- const playerAccount = requireAccount(playerAccountCandidate, player, `Starbase Player ${player} is missing for authorization Profile ${options.authorization.profile}. Register or refresh it in the Claim Stake's System before retrying.`);
584
- const profileFactionAccount = requireAccount(profileFactionAccountCandidate, profileFaction, `ProfileFaction ${profileFaction} is missing for authorization Profile ${options.authorization.profile}. Choose or repair the Profile faction, refresh it, and retry building changes.`);
585
- const currentSystem = starSystemAccountDefinition.decode(systemAccount);
586
- const currentPlayer = starbasePlayerAccountDefinition.decode(playerAccount);
757
+ const { currentPlayer, currentSystem, extraAccounts: [ownershipAccount, profileFactionCandidate], player, playerAccount, starbase, systemAccount, } = await readStakeStarbase(ctx, stake, game, system, character, profile, action, [ownership, profileFaction]);
758
+ requireStakeRegistered(planetBody.details.claimedPlots, stake, starbase.level, action);
759
+ const profileFactionAccount = requireAccount(profileFactionCandidate, profileFaction, `ProfileFaction ${profileFaction} is missing for authorization Profile ${profile}. Choose or repair the Profile faction, refresh it, and retry building changes.`);
587
760
  const currentProfileFaction = profileFactionIdentityAccountDefinition.decode(profileFactionAccount);
588
- if (currentSystem.game !== game ||
589
- currentSystem.sequenceId !== stake.systemSeqId ||
590
- currentSystem.starbase === undefined ||
591
- (await deriveStarSystemAddress(game, currentSystem.systemId)) !== system) {
592
- fail(`Star System ${system} does not match the configured Game, canonical identity, Claim Stake generation, or an active nested Starbase. Refresh the System and retry building changes.`, system);
761
+ if (currentProfileFaction.profile !== profile) {
762
+ fail(`ProfileFaction ${profileFaction} does not match authorization Profile ${profile}. Refresh or repair the Profile faction before retrying building changes.`, profileFaction);
593
763
  }
594
- if (currentPlayer.playerProfile !== options.authorization.profile ||
595
- currentPlayer.character !== character ||
596
- currentPlayer.system !== system) {
597
- fail(`Starbase Player ${player} does not match the authorization Profile, canonical Character, and Claim Stake System. Refresh it and retry building changes.`, player);
598
- }
599
- if (currentProfileFaction.profile !== options.authorization.profile) {
600
- fail(`ProfileFaction ${profileFaction} does not match authorization Profile ${options.authorization.profile}. Refresh or repair the Profile faction before retrying building changes.`, profileFaction);
601
- }
602
- let controllerFactionId = LEGACY_CONTROLLER_ID[currentSystem.starbase.owner];
764
+ let controllerFactionId = factionValue(starbase.owner);
603
765
  let ownershipPrecondition;
604
766
  if (ownershipAccount === undefined) {
605
- ownershipPrecondition = absentPrecondition(ownership, `The canonical Starbase FactionOwnership ${ownership} remains unallocated so the chain applies its legacy controller gate.`);
767
+ ownershipPrecondition = accountAbsentPrecondition(ownership, `The canonical Starbase FactionOwnership ${ownership} remains unallocated so the chain applies its legacy controller gate.`);
606
768
  }
607
769
  else {
608
770
  const currentOwnership = factionOwnershipAccountDefinition.decode(ownershipAccount);
@@ -614,7 +776,7 @@ export async function planClaimStakeBuildingChanges(ctx, stake, input) {
614
776
  }
615
777
  controllerFactionId =
616
778
  currentOwnership.controllingFactionId ?? controllerFactionId;
617
- ownershipPrecondition = await precondition(ownershipAccount, `The Starbase FactionOwnership remains assigned to System ${system} at its current generation.`);
779
+ ownershipPrecondition = await accountStatePrecondition(ownershipAccount, `The Starbase FactionOwnership remains assigned to System ${system} at its current generation.`);
618
780
  }
619
781
  if (currentProfileFaction.factionId !== controllerFactionId) {
620
782
  fail(`ProfileFaction ${profileFaction} faction ${String(currentProfileFaction.factionId)} does not match the current Starbase controller faction ${String(controllerFactionId)}. Use a Profile aligned with the current controller, refresh the faction relationship, and retry building changes.`, profileFaction);
@@ -647,6 +809,9 @@ export async function planClaimStakeBuildingChanges(ctx, stake, input) {
647
809
  if (definition === undefined) {
648
810
  fail(`Building ${String(change.buildingId)} has no definition in the current Game catalog. Refresh the definitions and choose a valid building before retrying.`, game);
649
811
  }
812
+ if (change.kind === 'remove' && definition.hubValue > 0) {
813
+ fail(`Building ${definition.name} is Claim Stake ${stake.address}'s bundled hub and cannot be removed. Remove other buildings, or deconstruct the whole Claim Stake, instead.`, stake.address);
814
+ }
650
815
  const current = result.get(change.buildingId) ?? 0;
651
816
  const next = change.kind === 'add'
652
817
  ? current + change.quantity
@@ -664,10 +829,15 @@ export async function planClaimStakeBuildingChanges(ctx, stake, input) {
664
829
  descriptions.push(buildingDescription(change, definition.name));
665
830
  }
666
831
  let usedSlots = 0;
667
- let crewSlots = 0;
668
832
  let neededCrew = 0;
833
+ let power = 0;
834
+ let hubs = 0;
669
835
  let resourceStorageRaw = 0n;
670
- const providedTags = new Set(claimDefinition.providedTagIds);
836
+ // Tags come from the Claim Stake definition, the Planet and every building.
837
+ const providedTags = new Set([
838
+ ...claimDefinition.providedTagIds,
839
+ ...planetBody.details.providedTags,
840
+ ]);
671
841
  for (const [buildingId, quantity] of result) {
672
842
  const definition = definitions.get(buildingId);
673
843
  if (definition === undefined) {
@@ -680,9 +850,15 @@ export async function planClaimStakeBuildingChanges(ctx, stake, input) {
680
850
  definition.hubValue !== claimDefinition.hubValue) {
681
851
  fail(`Hub building ${definition.name} does not match Claim Stake ${claimDefinition.name}'s hub value. Choose the matching hub before retrying.`, stake.address);
682
852
  }
853
+ if (definition.hubValue > 0) {
854
+ hubs += 1;
855
+ if (quantity !== 1) {
856
+ fail(`The resulting Claim Stake design has ${String(quantity)} of hub building ${definition.name}; a Claim Stake holds exactly one hub. Remove the extra hub before retrying.`, stake.address);
857
+ }
858
+ }
683
859
  usedSlots += definition.slots * quantity;
684
- crewSlots += definition.crewSlots * quantity;
685
860
  neededCrew += definition.neededCrew * quantity;
861
+ power += definition.power * quantity;
686
862
  resourceStorageRaw += definition.resourceStorageRaw * BigInt(quantity);
687
863
  if (neededCrew > U16_MAX) {
688
864
  fail(`The resulting Claim Stake design has ${String(neededCrew)} needed crew, exceeding the ${String(U16_MAX)} maximum representable by its unsigned u16 needed-crew field. Remove crew-requiring buildings and retry.`, stake.address);
@@ -695,8 +871,11 @@ export async function planClaimStakeBuildingChanges(ctx, stake, input) {
695
871
  if (usedSlots > claimDefinition.slots) {
696
872
  fail(`Building changes require ${String(usedSlots)} slots, exceeding Claim Stake ${claimDefinition.name}'s ${String(claimDefinition.slots)} slot capacity. Remove buildings and retry.`, stake.address);
697
873
  }
698
- if (neededCrew > crewSlots) {
699
- fail(`Building changes require ${String(neededCrew)} crew, exceeding the resulting ${String(crewSlots)} crew capacity. Add crew-slot capacity or remove crew-requiring buildings before retrying.`, stake.address);
874
+ if (hubs > 1) {
875
+ fail(`The resulting Claim Stake design has ${String(hubs)} hub buildings; a Claim Stake holds exactly one hub. Remove the extra hubs before retrying.`, stake.address);
876
+ }
877
+ if (power < 0) {
878
+ fail(`The resulting Claim Stake design uses ${String(-power)} more power than its buildings generate. Add power-generating buildings or remove power consumers before retrying.`, stake.address);
700
879
  }
701
880
  for (const [buildingId] of result) {
702
881
  const definition = definitions.get(buildingId);
@@ -714,10 +893,7 @@ export async function planClaimStakeBuildingChanges(ctx, stake, input) {
714
893
  if (addedCrew > availableCrew) {
715
894
  fail(`Building additions require ${String(addedCrew)} crew, but Starbase Player ${player} has only ${String(availableCrew)} known crew capacity available. Free crew, refresh the Starbase Player, and retry.`, player);
716
895
  }
717
- const cargo = new Map(currentStake.production.cargoPod.amounts.map(({ cargoId, amount }) => [
718
- cargoId,
719
- amount,
720
- ]));
896
+ const cargo = new Map(currentPlayer.cargo.amounts.map(({ cargoId, amount }) => [cargoId, amount]));
721
897
  const constructionCosts = new Map();
722
898
  for (const change of options.buildingChanges) {
723
899
  if (change.kind !== 'add')
@@ -732,7 +908,7 @@ export async function planClaimStakeBuildingChanges(ctx, stake, input) {
732
908
  const available = cargo.get(cargoId) ?? 0n;
733
909
  if (required > available) {
734
910
  const snapshotItem = stake.resources.inventory.items.find(({ id }) => id === cargoId);
735
- fail(`Building construction requires ${String(required)} raw ${snapshotItem?.name ?? `cargo ${String(cargoId)}`}, but Claim Stake ${stake.address} has only ${String(available)} known. Add construction cargo, refresh the Claim Stake, and retry.`, stake.address);
911
+ fail(`Building construction requires ${String(required)} raw ${snapshotItem?.name ?? `cargo ${String(cargoId)}`}, but Starbase Player ${player} has only ${String(available)} known in Starbase cargo. Construction cargo is taken from the Starbase, not from the Claim Stake. Deposit it at the Starbase, refresh the Starbase Player, and retry.`, player);
736
912
  }
737
913
  }
738
914
  if (currentStake.production.resourceCapacityRaw !==
@@ -744,38 +920,47 @@ export async function planClaimStakeBuildingChanges(ctx, stake, input) {
744
920
  if (currentStake.production.cargoPod.storageCost > resourceStorageRaw) {
745
921
  fail(`The resulting Claim Stake design provides ${String(resourceStorageRaw)} raw storage capacity, below the fresh known cargo usage of ${String(currentStake.production.cargoPod.storageCost)}. Keep enough storage buildings, remove cargo, refresh the Claim Stake, and retry building changes.`, stake.address);
746
922
  }
747
- const currencyCache = await deriveCurrencyCacheAddress(game);
748
- const description = `Change buildings on Claim Stake ${stake.address}: ${descriptions.join('; ')}.`;
749
- return createPlan({
923
+ const currencyCache = await claimStakeCurrencyCache(game);
924
+ const xp = await resolveClaimStakeXp(ctx, game, stake.address);
925
+ const instruction = adaptClaimStakeBuildingChangesInstruction({
926
+ ...xp.accounts,
927
+ authorization: options.authorization,
928
+ buildingChanges: options.buildingChanges.map(({ buildingId, kind, quantity }) => [buildingId, kind === 'add' ? quantity : -quantity]),
929
+ celestialBody: stake.body.address,
930
+ claimStakeInstance: stake.address,
931
+ currencyCache,
932
+ factionOwnership: ownership,
933
+ game,
934
+ profileFaction,
935
+ ...rent.accounts,
936
+ sequenceId,
937
+ starbasePlayer: player,
938
+ system,
939
+ });
940
+ // The grown design is funded from the Profile vault.
941
+ const storage = await planClaimStakeStorage(ctx, instruction, profile, [stakeAccount], 'Claim Stake building changes');
942
+ const description = `Change buildings on Claim Stake ${stake.address}: ${descriptions.join('; ')}. ${storage.disclosure}`;
943
+ return buildPlannerPlan({
750
944
  kind: 'claim-stakes.building-changes',
751
945
  preconditions: [
752
946
  ...(await Promise.all([
753
- precondition(freshGame.account, 'The configured Game retains the exact Claim Stake and building definitions.'),
754
- precondition(stakeAccount, `Claim Stake ${stake.address} retains its ownership, design sequence, buildings, cargo, crew, and Body relationship.`),
755
- precondition(bodyAccount, `The Claim Stake Planet remains assigned to System ${system} at the current generation.`),
756
- precondition(characterAccount, `The canonical Character remains assigned to Profile ${options.authorization.profile} in the configured Game.`),
757
- precondition(systemAccount, `Star System ${currentSystem.name} retains its configured Game, sequence, and nested Starbase.`),
758
- precondition(playerAccount, `Starbase Player ${player} retains its Profile, Character, System, and available crew.`),
759
- precondition(profileFactionAccount, `ProfileFaction ${profileFaction} remains assigned to Profile ${options.authorization.profile} and aligned with the current Starbase controller.`),
947
+ accountStatePrecondition(freshGame.account, 'The configured Game retains the exact Claim Stake and building definitions.'),
948
+ accountStatePrecondition(stakeAccount, `Claim Stake ${stake.address} retains its ownership, design sequence, buildings, cargo, crew, and Body relationship.`),
949
+ planetPrecondition(bodyAccount, system),
950
+ characterPrecondition(characterAccount, profile),
951
+ systemPrecondition(systemAccount, currentSystem.name),
952
+ playerPrecondition(playerAccount, player, 'Profile, Character, System, and available crew'),
953
+ accountStatePrecondition(profileFactionAccount, `ProfileFaction ${profileFaction} remains assigned to Profile ${profile} and aligned with the current Starbase controller.`),
760
954
  ])),
761
955
  ownershipPrecondition,
956
+ xp.guard,
957
+ ...rent.guards,
958
+ storage.guard,
762
959
  ],
763
960
  steps: [
764
961
  {
765
962
  describes: description,
766
- instruction: adaptClaimStakeBuildingChangesInstruction({
767
- authorization: options.authorization,
768
- buildingChanges: options.buildingChanges.map(({ buildingId, kind, quantity }) => [buildingId, kind === 'add' ? quantity : -quantity]),
769
- celestialBody: stake.body.address,
770
- claimStakeInstance: stake.address,
771
- currencyCache,
772
- factionOwnership: ownership,
773
- game,
774
- profileFaction,
775
- sequenceId,
776
- starbasePlayer: player,
777
- system,
778
- }),
963
+ instruction,
779
964
  signers: [options.authorization.authority],
780
965
  },
781
966
  ],
@@ -789,6 +974,20 @@ export async function planClaimStakeBuildingChanges(ctx, stake, input) {
789
974
  * CurrencyCache, and a known positive rent balance. Rent timing and the final
790
975
  * crew/building validation remain chain-authoritative.
791
976
  *
977
+ * Finalization can draw Profile storage this Plan does not bound: a removed
978
+ * building's refund can open a Starbase Player cargo line, and a final layout
979
+ * that produces a new resource raises the stake's rent floor. The program
980
+ * funds both from the Profile vault with the signing key, so simulate before
981
+ * signing.
982
+ *
983
+ * Requires Game 2 and its existing canonical Claim Stake XP ledger.
984
+ * The Character XP budget and configuration must also be reconciled.
985
+ * Missing XP state must be repaired outside this Plan; no accounts are created.
986
+ *
987
+ * Requires Disabled rent: the Game's rent catalog and the Claim Stake's rent
988
+ * settlement authority must not exist, and both stay guarded absent until
989
+ * signing. The instruction carries no rent history.
990
+ *
792
991
  * @example
793
992
  * ```ts
794
993
  * declare const ctx: SageContext;
@@ -804,22 +1003,19 @@ export async function planClaimStakeBuildingChanges(ctx, stake, input) {
804
1003
  export async function planFinalizeClaimStakeBuildingChanges(ctx, stake, input) {
805
1004
  const options = validateFinalizeBuildingChangeOptions(input);
806
1005
  const game = requireGameAddress(ctx);
1006
+ const { profile } = options.authorization;
807
1007
  if (stake.state.kind !== 'design') {
808
1008
  fail(`Claim Stake ${stake.address} is not in design state. Refresh it and finalize only a pending building design.`, stake.address);
809
1009
  }
810
- if (stake.game !== game) {
811
- fail(`Claim Stake ${stake.address} does not match the configured Game ${game}. Refresh it through this context and retry.`, stake.address);
812
- }
813
- const character = await findCharacterPda(options.authorization.profile, game);
814
- if (stake.ownerCharacter.address !== character) {
815
- fail(`Claim Stake ${stake.address} does not belong to authorization Profile ${options.authorization.profile}'s canonical Character ${character}. Choose an owned Claim Stake, refresh it, and retry.`, stake.address);
816
- }
1010
+ const character = await requireOwnedStake(stake, game, profile);
817
1011
  if (stake.rentBalanceRaw <= 0n) {
818
1012
  fail(`Claim Stake ${stake.address} has a known delinquent rent balance of ${String(stake.rentBalanceRaw)} raw units. Top up rent, refresh the Claim Stake, and retry finalization.`, stake.address);
819
1013
  }
820
- const [freshGame, firstAccounts] = await Promise.all([
1014
+ const action = 'finalization';
1015
+ const [freshGame, loaded, rent] = await Promise.all([
821
1016
  readFreshGameClaimStakeConfiguration(ctx),
822
- readContextRawAccounts(ctx, [stake.address, stake.body.address, character]),
1017
+ readOwnedStake(ctx, stake, action, [character]),
1018
+ requireDisabledStakeRent(ctx, game, stake.address, action),
823
1019
  ]);
824
1020
  if (freshGame === undefined) {
825
1021
  fail(`Configured Game ${game} is missing. Refresh the Game catalog and retry finalization.`, game);
@@ -828,81 +1024,51 @@ export async function planFinalizeClaimStakeBuildingChanges(ctx, stake, input) {
828
1024
  freshGame.data.buildings.sequenceId !== stake.buildingDefinitionSeqId) {
829
1025
  fail(`Claim Stake ${stake.address} definition sequence is stale against the current Game catalog. Refresh the Claim Stake and definitions to rule out stale local data. If the on-chain generation still differs, use a Claim Stake matching the current catalog before retrying finalization.`, stake.address);
830
1026
  }
831
- const stakeAccount = requireAccount(firstAccounts[0], stake.address, `Claim Stake ${stake.address} is missing. Refresh it and retry finalization.`);
832
- const bodyAccount = requireAccount(firstAccounts[1], stake.body.address, `Celestial Body ${stake.body.address} is missing. Refresh the Claim Stake Body and retry finalization.`);
833
- const characterAccount = requireAccount(firstAccounts[2], character, `Canonical Character ${character} is missing. Refresh the Profile Character and retry finalization.`);
834
- const currentStake = claimStakeAccountDefinition.decode(stakeAccount);
835
- const currentBody = celestialBodyAccountDefinition.decode(bodyAccount);
836
- const currentCharacter = characterAccountDefinition.decode(characterAccount);
837
- if (currentStake.game !== game ||
838
- currentStake.body !== stake.body.address ||
839
- currentStake.ownerCharacter !== character ||
840
- currentStake.claimStakeDefinitionId !== stake.claimStakeDefinitionId ||
841
- currentStake.claimStakeDefinitionSeqId !==
842
- stake.claimStakeDefinitionSeqId ||
843
- currentStake.buildingDefinitionSeqId !== stake.buildingDefinitionSeqId ||
844
- currentStake.systemSeqId !== stake.systemSeqId ||
1027
+ const { bodyAccount, currentStake, stakeAccount } = loaded;
1028
+ if (!sameOwnedStake(currentStake, stake, game, profile) ||
845
1029
  currentStake.rentBalanceRaw !== stake.rentBalanceRaw ||
846
- !sameQuantities(currentStake.buildings, stake.buildings) ||
847
1030
  currentStake.state.kind !== 'design' ||
848
1031
  currentStake.state.sequenceId !== stake.state.sequenceId ||
849
1032
  !sameQuantities(currentStake.state.buildings, stake.state.buildings)) {
850
1033
  fail(`Claim Stake ${stake.address} changed or does not match its loaded ownership, Body, definitions, buildings, design sequence, rent, or System generation. Refresh it and retry finalization.`, stake.address);
851
1034
  }
852
- if (currentCharacter.playerProfile !== options.authorization.profile ||
853
- currentCharacter.game !== game) {
854
- fail(`Character ${character} does not match authorization Profile ${options.authorization.profile} and configured Game ${game}. Refresh the Character and retry finalization.`, character);
855
- }
856
- if (currentBody.kind !== 'planet' ||
857
- currentBody.game !== game ||
858
- currentBody.systemSequenceId !== stake.systemSeqId ||
859
- (await deriveCelestialBodyAddress(game, currentBody.id)) !==
860
- stake.body.address) {
861
- fail(`Celestial Body ${stake.body.address} does not match the Claim Stake's configured Game, canonical Planet identity, or System generation. Refresh the Body and Claim Stake before retrying finalization.`, stake.body.address);
862
- }
863
- const system = currentBody.system;
864
- const player = await deriveStarbasePlayerAddress(system, character);
865
- const currencyCache = await deriveCurrencyCacheAddress(game);
866
- const [systemCandidate, playerCandidate, currencyCandidate] = await readContextRawAccounts(ctx, [system, player, currencyCache]);
867
- const systemAccount = requireAccount(systemCandidate, system, `Star System ${system} is missing. Refresh the Claim Stake's System and retry finalization.`);
868
- const playerAccount = requireAccount(playerCandidate, player, `Starbase Player ${player} is missing for authorization Profile ${options.authorization.profile}. Register or refresh it in the Claim Stake's System before retrying finalization.`);
869
- const currencyAccount = requireAccount(currencyCandidate, currencyCache, `Canonical CurrencyCache ${currencyCache} is missing for configured Game ${game}. Refresh the Game currency configuration and retry finalization.`);
870
- const currentSystem = starSystemAccountDefinition.decode(systemAccount);
871
- const currentPlayer = starbasePlayerAccountDefinition.decode(playerAccount);
872
- if (currentSystem.game !== game ||
873
- currentSystem.sequenceId !== stake.systemSeqId ||
874
- currentSystem.starbase === undefined ||
875
- (await deriveStarSystemAddress(game, currentSystem.systemId)) !== system) {
876
- fail(`Star System ${system} does not match the configured Game, canonical identity, Claim Stake generation, or an active nested Starbase. Refresh the System and retry finalization.`, system);
877
- }
878
- if (currentPlayer.playerProfile !== options.authorization.profile ||
879
- currentPlayer.character !== character ||
880
- currentPlayer.system !== system) {
881
- fail(`Starbase Player ${player} does not match the authorization Profile, canonical Character, and Claim Stake System. Refresh it and retry finalization.`, player);
882
- }
883
- validateCurrencyCache(currencyAccount, game, currencyCache, 'finalization');
884
- const description = `Finalize building changes on Claim Stake ${stake.address}.`;
885
- return createPlan({
1035
+ requireCommittedCrewVersion(currentStake, stake, 'finalized');
1036
+ const planet = await requireDesignStakePlanet(loaded.currentBody, stake, game, action);
1037
+ const { system } = planet;
1038
+ const { account: characterAccount } = requireProfileCharacter(loaded.extraAccounts[0], character, profile, game, action);
1039
+ const currencyCache = await claimStakeCurrencyCache(game);
1040
+ const { currentSystem, extraAccounts: [currencyCandidate], player, playerAccount, starbase, systemAccount, } = await readStakeStarbase(ctx, stake, game, system, character, profile, action, [currencyCache]);
1041
+ requireStakeRegistered(planet.details.claimedPlots, stake, starbase.level, action);
1042
+ const currencyGuard = await requireCurrencyCache(currencyCandidate, currencyCache, game, action, 'Claim Stake finalization');
1043
+ const description = `Finalize building changes on Claim Stake ${stake.address}. A building refund that opens a Starbase Player cargo line, or a new resource that raises the stake's rent floor, is funded from the Profile vault with the signing key, which this Plan does not bound; simulate before signing.`;
1044
+ const xp = await resolveClaimStakeXp(ctx, game, stake.address, character);
1045
+ return buildPlannerPlan({
886
1046
  kind: 'claim-stakes.finalize-building-changes',
887
- preconditions: await Promise.all([
888
- precondition(freshGame.account, 'The configured Game remains current.'),
889
- precondition(stakeAccount, `Claim Stake ${stake.address} retains its ownership, design sequence, rent balance, Body, and System generation.`),
890
- precondition(characterAccount, `The canonical Character remains assigned to Profile ${options.authorization.profile} in the configured Game.`),
891
- precondition(bodyAccount, `The Claim Stake Planet remains assigned to System ${system} at the current generation.`),
892
- precondition(systemAccount, `Star System ${currentSystem.name} retains its configured Game, sequence, and nested Starbase.`),
893
- precondition(playerAccount, `Starbase Player ${player} retains its Profile, Character, and System.`),
894
- precondition(currencyAccount, 'The canonical CurrencyCache remains assigned to the configured Game.'),
895
- ]),
1047
+ preconditions: [
1048
+ xp.guard,
1049
+ ...(await Promise.all([
1050
+ accountStatePrecondition(freshGame.account, 'The configured Game remains current.'),
1051
+ accountStatePrecondition(stakeAccount, `Claim Stake ${stake.address} retains its ownership, design sequence, rent balance, Body, and System generation.`),
1052
+ characterPrecondition(characterAccount, profile),
1053
+ planetPrecondition(bodyAccount, system),
1054
+ systemPrecondition(systemAccount, currentSystem.name),
1055
+ playerPrecondition(playerAccount, player),
1056
+ ])),
1057
+ currencyGuard,
1058
+ ...rent.guards,
1059
+ ],
896
1060
  steps: [
897
1061
  {
898
1062
  describes: description,
899
1063
  instruction: adaptFinalizeClaimStakeBuildingChangesInstruction({
1064
+ ...xp.accounts,
900
1065
  authorization: options.authorization,
901
1066
  celestialBody: stake.body.address,
902
1067
  character,
903
1068
  claimStakeInstance: stake.address,
904
1069
  currencyCache,
905
1070
  game,
1071
+ ...rent.accounts,
906
1072
  systemAndStarbasePlayerStarbasePlayer: player,
907
1073
  systemAndStarbasePlayerSystem: system,
908
1074
  }),
@@ -920,6 +1086,13 @@ export async function planFinalizeClaimStakeBuildingChanges(ctx, stake, input) {
920
1086
  * state; rent arithmetic and concurrent state transitions remain
921
1087
  * chain-authoritative.
922
1088
  *
1089
+ * Requires Game 2 and its existing canonical Claim Stake XP ledger.
1090
+ * Missing XP state must be repaired outside this Plan; no accounts are created.
1091
+ *
1092
+ * Requires Disabled rent: the Game's rent catalog and the Claim Stake's rent
1093
+ * settlement authority must not exist, and both stay guarded absent until
1094
+ * signing. The instruction carries no rent history.
1095
+ *
923
1096
  * @example
924
1097
  * ```ts
925
1098
  * declare const ctx: SageContext;
@@ -934,85 +1107,54 @@ export async function planFinalizeClaimStakeBuildingChanges(ctx, stake, input) {
934
1107
  export async function planCancelClaimStakeBuildingChanges(ctx, stake, input) {
935
1108
  const options = validateCancelBuildingChangeOptions(input);
936
1109
  const game = requireGameAddress(ctx);
1110
+ const { profile } = options.authorization;
937
1111
  if (stake.state.kind !== 'design') {
938
1112
  fail(`Claim Stake ${stake.address} is not in design state. Refresh it and cancel only a pending building design.`, stake.address);
939
1113
  }
940
- if (stake.game !== game) {
941
- fail(`Claim Stake ${stake.address} does not match the configured Game ${game}. Refresh it through this context and retry.`, stake.address);
942
- }
943
- const character = await findCharacterPda(options.authorization.profile, game);
944
- if (stake.ownerCharacter.address !== character) {
945
- fail(`Claim Stake ${stake.address} does not belong to authorization Profile ${options.authorization.profile}'s canonical Character ${character}. Choose an owned Claim Stake, refresh it, and retry.`, stake.address);
946
- }
947
- const firstAccounts = await readContextRawAccounts(ctx, [
948
- stake.address,
949
- stake.body.address,
1114
+ const character = await requireOwnedStake(stake, game, profile);
1115
+ const action = 'cancellation';
1116
+ const [{ bodyAccount, currentBody, currentStake, stakeAccount }, rent] = await Promise.all([
1117
+ readOwnedStake(ctx, stake, action),
1118
+ requireDisabledStakeRent(ctx, game, stake.address, action),
950
1119
  ]);
951
- const stakeAccount = requireAccount(firstAccounts[0], stake.address, `Claim Stake ${stake.address} is missing. Refresh it and retry cancellation.`);
952
- const bodyAccount = requireAccount(firstAccounts[1], stake.body.address, `Celestial Body ${stake.body.address} is missing. Refresh the Claim Stake Body and retry cancellation.`);
953
- const currentStake = claimStakeAccountDefinition.decode(stakeAccount);
954
- const currentBody = celestialBodyAccountDefinition.decode(bodyAccount);
955
- if (currentStake.game !== game ||
956
- currentStake.body !== stake.body.address ||
957
- currentStake.ownerCharacter !== character ||
958
- currentStake.claimStakeDefinitionId !== stake.claimStakeDefinitionId ||
959
- currentStake.claimStakeDefinitionSeqId !==
960
- stake.claimStakeDefinitionSeqId ||
961
- currentStake.buildingDefinitionSeqId !== stake.buildingDefinitionSeqId ||
962
- currentStake.systemSeqId !== stake.systemSeqId ||
963
- !sameQuantities(currentStake.buildings, stake.buildings) ||
1120
+ if (!sameOwnedStake(currentStake, stake, game, profile) ||
964
1121
  currentStake.state.kind !== 'design' ||
965
1122
  currentStake.state.sequenceId !== stake.state.sequenceId ||
966
1123
  !sameQuantities(currentStake.state.buildings, stake.state.buildings)) {
967
1124
  fail(`Claim Stake ${stake.address} changed or does not match its loaded ownership, Body, definitions, buildings, design sequence, or System generation. Refresh it and retry cancellation.`, stake.address);
968
1125
  }
969
- if (currentBody.kind !== 'planet' ||
970
- currentBody.game !== game ||
971
- currentBody.systemSequenceId !== stake.systemSeqId ||
972
- (await deriveCelestialBodyAddress(game, currentBody.id)) !==
973
- stake.body.address) {
974
- fail(`Celestial Body ${stake.body.address} does not match the Claim Stake's configured Game, canonical Planet identity, or System generation. Refresh the Body and Claim Stake before retrying cancellation.`, stake.body.address);
975
- }
976
- const system = currentBody.system;
977
- const player = await deriveStarbasePlayerAddress(system, character);
978
- const currencyCache = await deriveCurrencyCacheAddress(game);
979
- const [systemCandidate, playerCandidate, currencyCandidate] = await readContextRawAccounts(ctx, [system, player, currencyCache]);
980
- const systemAccount = requireAccount(systemCandidate, system, `Star System ${system} is missing. Refresh the Claim Stake's System and retry cancellation.`);
981
- const playerAccount = requireAccount(playerCandidate, player, `Starbase Player ${player} is missing for authorization Profile ${options.authorization.profile}. Register or refresh it in the Claim Stake's System before retrying cancellation.`);
982
- const currencyAccount = requireAccount(currencyCandidate, currencyCache, `Canonical CurrencyCache ${currencyCache} is missing for configured Game ${game}. Refresh the Game currency configuration and retry cancellation.`);
983
- const currentSystem = starSystemAccountDefinition.decode(systemAccount);
984
- const currentPlayer = starbasePlayerAccountDefinition.decode(playerAccount);
985
- if (currentSystem.game !== game ||
986
- currentSystem.sequenceId !== stake.systemSeqId ||
987
- currentSystem.starbase === undefined ||
988
- (await deriveStarSystemAddress(game, currentSystem.systemId)) !== system) {
989
- fail(`Star System ${system} does not match the configured Game, canonical identity, Claim Stake generation, or an active nested Starbase. Refresh the System and retry cancellation.`, system);
990
- }
991
- if (currentPlayer.playerProfile !== options.authorization.profile ||
992
- currentPlayer.character !== character ||
993
- currentPlayer.system !== system) {
994
- fail(`Starbase Player ${player} does not match the authorization Profile, canonical Character, and Claim Stake System. Refresh it and retry cancellation.`, player);
995
- }
996
- validateCurrencyCache(currencyAccount, game, currencyCache, 'cancellation');
1126
+ const planet = await requireDesignStakePlanet(currentBody, stake, game, action);
1127
+ const { system } = planet;
1128
+ const currencyCache = await claimStakeCurrencyCache(game);
1129
+ const { currentSystem, extraAccounts: [currencyCandidate], player, playerAccount, starbase, systemAccount, } = await readStakeStarbase(ctx, stake, game, system, character, profile, action, [currencyCache]);
1130
+ requireStakeRegistered(planet.details.claimedPlots, stake, starbase.level, action);
1131
+ const currencyGuard = await requireCurrencyCache(currencyCandidate, currencyCache, game, action, 'Claim Stake cancellation');
997
1132
  const description = `Cancel the pending building design on Claim Stake ${stake.address} and return it to active state.`;
998
- return createPlan({
1133
+ const xp = await resolveClaimStakeXp(ctx, game, stake.address);
1134
+ return buildPlannerPlan({
999
1135
  kind: 'claim-stakes.cancel-building-changes',
1000
- preconditions: await Promise.all([
1001
- precondition(stakeAccount, `Claim Stake ${stake.address} retains its ownership, design sequence, buildings, Body, and System generation.`),
1002
- precondition(bodyAccount, `The Claim Stake Planet remains assigned to System ${system} at the current generation.`),
1003
- precondition(systemAccount, `Star System ${currentSystem.name} retains its configured Game, sequence, and nested Starbase.`),
1004
- precondition(playerAccount, `Starbase Player ${player} retains its Profile, Character, and System.`),
1005
- precondition(currencyAccount, 'The canonical CurrencyCache remains assigned to the configured Game.'),
1006
- ]),
1136
+ preconditions: [
1137
+ xp.guard,
1138
+ ...(await Promise.all([
1139
+ accountStatePrecondition(stakeAccount, `Claim Stake ${stake.address} retains its ownership, design sequence, buildings, Body, and System generation.`),
1140
+ planetPrecondition(bodyAccount, system),
1141
+ systemPrecondition(systemAccount, currentSystem.name),
1142
+ playerPrecondition(playerAccount, player),
1143
+ ])),
1144
+ currencyGuard,
1145
+ ...rent.guards,
1146
+ ],
1007
1147
  steps: [
1008
1148
  {
1009
1149
  describes: description,
1010
1150
  instruction: adaptCancelClaimStakeBuildingChangesInstruction({
1151
+ ...xp.accounts,
1011
1152
  authorization: options.authorization,
1012
1153
  celestialBody: stake.body.address,
1013
1154
  claimStakeInstance: stake.address,
1014
1155
  currencyCache,
1015
1156
  game,
1157
+ ...rent.accounts,
1016
1158
  starbasePlayer: player,
1017
1159
  system,
1018
1160
  }),
@@ -1024,13 +1166,19 @@ export async function planCancelClaimStakeBuildingChanges(ctx, stake, input) {
1024
1166
  }
1025
1167
  /** Plans deconstruction of one owned active Claim Stake.
1026
1168
  *
1027
- * The planner derives the current Planet chronology index and exact plot bucket
1028
- * from the Claim Stake definition tier and fresh Body plot matrix. The only
1169
+ * The planner derives the plot level from the fresh Planet plot row that
1170
+ * records the stake, and the index from its position in that row. The only
1029
1171
  * remaining building must be the definition-compatible zero-cost bundled hub,
1030
1172
  * which deconstruction discards.
1031
1173
  * Rent settlement, cargo return, account closure, and concurrent races remain
1032
1174
  * chain-authoritative; the Plan does not estimate a refund.
1033
1175
  *
1176
+ * Requires Disabled rent: the Game's rent catalog and the Claim Stake's rent
1177
+ * settlement authority must not exist, and both stay guarded absent until
1178
+ * signing. The instruction carries no rent history. Deconstruction also
1179
+ * requires the canonical Currency Cache and closes the Claim Stake's XP ledger;
1180
+ * a Claim Stake without a ledger accrued no XP.
1181
+ *
1034
1182
  * @example
1035
1183
  * ```ts
1036
1184
  * declare const ctx: SageContext;
@@ -1043,46 +1191,27 @@ export async function planCancelClaimStakeBuildingChanges(ctx, stake, input) {
1043
1191
  export async function planDeconstructClaimStake(ctx, stake, input) {
1044
1192
  const options = validateDeconstructOptions(input);
1045
1193
  const game = requireGameAddress(ctx);
1194
+ const { profile } = options.authorization;
1046
1195
  if (stake.state.kind !== 'active') {
1047
1196
  fail(`Claim Stake ${stake.address} is not in active state. Refresh it, finalize or cancel any pending design, and retry deconstruction.`, stake.address);
1048
1197
  }
1049
- if (stake.game !== game) {
1050
- fail(`Claim Stake ${stake.address} does not match the configured Game ${game}. Refresh it through this context and retry.`, stake.address);
1051
- }
1052
- const character = await findCharacterPda(options.authorization.profile, game);
1053
- if (stake.ownerCharacter.address !== character) {
1054
- fail(`Claim Stake ${stake.address} does not belong to authorization Profile ${options.authorization.profile}'s canonical Character ${character}. Choose an owned Claim Stake, refresh it, and retry.`, stake.address);
1055
- }
1056
- const [freshGame, firstAccounts] = await Promise.all([
1198
+ const character = await requireOwnedStake(stake, game, profile);
1199
+ const action = 'deconstruction';
1200
+ const [freshGame, { bodyAccount, currentBody, currentStake, stakeAccount }, rent,] = await Promise.all([
1057
1201
  readFreshGameClaimStakeConfiguration(ctx),
1058
- readContextRawAccounts(ctx, [stake.address, stake.body.address]),
1202
+ readOwnedStake(ctx, stake, action),
1203
+ requireDisabledStakeRent(ctx, game, stake.address, action),
1059
1204
  ]);
1060
1205
  if (freshGame === undefined) {
1061
1206
  fail(`Configured Game ${game} is missing. Refresh the Game definitions and retry deconstruction.`, game);
1062
1207
  }
1063
- const stakeAccount = requireAccount(firstAccounts[0], stake.address, `Claim Stake ${stake.address} is missing. Refresh it and retry deconstruction.`);
1064
- const bodyAccount = requireAccount(firstAccounts[1], stake.body.address, `Celestial Body ${stake.body.address} is missing. Refresh the Claim Stake Body and retry deconstruction.`);
1065
- const currentStake = claimStakeAccountDefinition.decode(stakeAccount);
1066
- const currentBody = decodeIndexedCelestialBodyAccount(bodyAccount);
1067
- if (currentStake.game !== game ||
1068
- currentStake.body !== stake.body.address ||
1069
- currentStake.ownerCharacter !== character ||
1070
- currentStake.claimStakeDefinitionId !== stake.claimStakeDefinitionId ||
1071
- currentStake.claimStakeDefinitionSeqId !==
1072
- stake.claimStakeDefinitionSeqId ||
1073
- currentStake.buildingDefinitionSeqId !== stake.buildingDefinitionSeqId ||
1074
- currentStake.systemSeqId !== stake.systemSeqId ||
1075
- !sameQuantities(currentStake.buildings, stake.buildings) ||
1208
+ if (!sameOwnedStake(currentStake, stake, game, profile) ||
1076
1209
  currentStake.state.kind !== 'active') {
1077
1210
  fail(`Claim Stake ${stake.address} changed or does not match its loaded ownership, Body, definitions, buildings, active state, or System generation. Refresh it and retry deconstruction.`, stake.address);
1078
1211
  }
1079
- if (currentBody.kind !== 'planet' ||
1080
- currentBody.game !== game ||
1081
- currentBody.systemSequenceId !== stake.systemSeqId ||
1082
- (await deriveCelestialBodyAddress(game, currentBody.id)) !==
1083
- stake.body.address) {
1084
- fail(`Celestial Body ${stake.body.address} does not match the Claim Stake's configured Game, canonical Planet identity, or System generation. Refresh the Body and Claim Stake before retrying deconstruction.`, stake.body.address);
1085
- }
1212
+ requireCommittedCrewVersion(currentStake, stake, 'deconstructed');
1213
+ const planet = await requireStakePlanet(currentBody, stake, game, action);
1214
+ const { system } = planet;
1086
1215
  if (freshGame.data.claimStakes.sequenceId !== stake.claimStakeDefinitionSeqId) {
1087
1216
  fail(`Claim Stake ${stake.address} definition generation is stale against the current Game catalog. Refresh the Claim Stake and definitions to rule out stale local data. If the on-chain generation still differs, use a Claim Stake matching the current catalog before retrying deconstruction.`, stake.address);
1088
1217
  }
@@ -1104,92 +1233,84 @@ export async function planDeconstructClaimStake(ctx, stake, input) {
1104
1233
  bundledHub.hubValue !== claimDefinition.hubValue ||
1105
1234
  bundledHub.minimumTier !== claimDefinition.tier ||
1106
1235
  bundledHub.constructionCost.length !== 0) {
1236
+ // Only a building with a construction cost is player-funded; a zero-cost
1237
+ // hub from another definition family is a foreign bundled hub.
1107
1238
  const hasPlayerFundedBuilding = stake.buildings.some((building) => {
1108
1239
  const definition = definitions.get(building.buildingId);
1109
- return (definition === undefined ||
1110
- definition.hubValue !== claimDefinition.hubValue ||
1111
- definition.minimumTier !== claimDefinition.tier ||
1112
- definition.constructionCost.length !== 0);
1240
+ return (definition !== undefined && definition.constructionCost.length !== 0);
1113
1241
  });
1114
1242
  fail(hasPlayerFundedBuilding
1115
1243
  ? `Claim Stake ${stake.address} still contains a player-funded building. Remove and finalize every player-funded building so only its verified bundled hub remains before retrying deconstruction.`
1116
- : `Claim Stake ${stake.address} does not contain exactly one verified bundled hub. Refresh it and restore the definition-compatible bundled hub before retrying deconstruction.`, stake.address);
1117
- }
1118
- const groups = currentBody.details.claimedPlots.filter(({ key }) => key === claimDefinition.id);
1119
- if (groups.length !== 1) {
1120
- fail(`Claim Stake definition ${String(claimDefinition.id)} does not have exactly one coherent group in Planet ${stake.body.address}'s Claim Stake Body data. Refresh the Body before retrying deconstruction.`, stake.body.address);
1121
- }
1122
- const group = groups[0];
1123
- const chronologyMatches = group.chronoAddresses
1124
- .map((address, index) => ({ address, index }))
1125
- .filter(({ address }) => address === stake.address);
1126
- if (chronologyMatches.length !== 1) {
1127
- fail(`Claim Stake ${stake.address} must appear exactly once in the Planet chronology before its deconstruction index can be derived. Refresh the Body and retry.`, stake.body.address);
1128
- }
1129
- const activeStructures = group.structures.filter(({ address, deactivationTimeUnixSeconds }) => address === stake.address && deactivationTimeUnixSeconds === 0n);
1244
+ : bundledHub === undefined
1245
+ ? `Claim Stake ${stake.address} does not contain exactly one verified bundled hub. Refresh it and restore the definition-compatible bundled hub before retrying deconstruction.`
1246
+ : `Claim Stake ${stake.address} holds ${bundledHub.name} (${String(bundledHub.id)}), which is not the zero-cost ${claimDefinition.tier} bundled hub for Claim Stake definition ${String(claimDefinition.id)} (${claimDefinition.name}). Refresh the Claim Stake and Game definitions so the recorded hub matches its definition before retrying deconstruction.`, stake.address);
1247
+ }
1248
+ // Each plot row is a Starbase level; the stake's row and its position in
1249
+ // that row's chronology give the deconstruction level and index.
1250
+ const positions = claimStakePlotPositions(planet.details.claimedPlots, stake.address);
1251
+ if (positions.length !== 1) {
1252
+ fail(`Claim Stake ${stake.address} must appear exactly once in Planet ${stake.body.address}'s plot chronology before its deconstruction level and index can be derived. Refresh the Body and retry.`, stake.body.address);
1253
+ }
1254
+ const { index: claimStakeIndex, row } = positions[0];
1255
+ const rowGroups = planet.details.claimedPlots.filter(({ key }) => key === row);
1256
+ if (!Number.isInteger(row) ||
1257
+ row < 0 ||
1258
+ row >= CLAIM_STAKE_PLOT_ROWS ||
1259
+ rowGroups.length !== 1) {
1260
+ fail(`Planet ${stake.body.address} records Claim Stake ${stake.address} in plot row ${String(row)}, which is not exactly one Starbase level row. Refresh the Body and verify the pinned binding version before retrying deconstruction.`, stake.body.address);
1261
+ }
1262
+ const activeStructures = rowGroups[0].structures.filter(({ address, deactivationTimeUnixSeconds }) => address === stake.address && deactivationTimeUnixSeconds === 0n);
1130
1263
  if (activeStructures.length !== 1) {
1131
1264
  fail(`Claim Stake ${stake.address} is not represented exactly once as an active structure in the Planet Body data. Refresh the Body and retry deconstruction.`, stake.body.address);
1132
1265
  }
1133
- const claimStakeIndex = chronologyMatches[0].index;
1134
- const tierIndex = TIER_INDEX[claimDefinition.tier];
1135
- const plotCapacities = currentBody.details.claimStakePlots.map((row) => row[tierIndex]);
1136
- if (plotCapacities.some((capacity, index) => index > 0 && capacity < plotCapacities[index - 1])) {
1137
- fail(`Planet ${stake.body.address} Claim Stake plot capacity is not coherent across Starbase levels for definition tier ${claimDefinition.tier}. Refresh the Body and verify the pinned binding version before retrying deconstruction.`, stake.body.address);
1138
- }
1139
- if (group.chronoAddresses.length > plotCapacities[plotCapacities.length - 1]) {
1140
- fail(`Planet ${stake.body.address} Claim Stake plot capacity does not cover its definition-group chronology. Refresh the Body before retrying deconstruction.`, stake.body.address);
1141
- }
1142
- const plotLevelIndex = plotCapacities.findIndex((capacity) => claimStakeIndex < capacity);
1143
- const claimStakeLevel = CLAIM_STAKE_PLOT_LEVELS[plotLevelIndex];
1144
- const system = currentBody.system;
1145
- const player = await deriveStarbasePlayerAddress(system, character);
1146
- const [characterCandidate, systemCandidate, playerCandidate] = await readContextRawAccounts(ctx, [character, system, player]);
1147
- const characterAccount = requireAccount(characterCandidate, character, `Canonical Character ${character} is missing. Refresh the Profile Character and retry deconstruction.`);
1148
- const systemAccount = requireAccount(systemCandidate, system, `Star System ${system} is missing. Refresh the Claim Stake's System and retry deconstruction.`);
1149
- const playerAccount = requireAccount(playerCandidate, player, `Starbase Player ${player} is missing for authorization Profile ${options.authorization.profile}. Register or refresh it in the Claim Stake's System before retrying deconstruction.`);
1150
- const currentCharacter = characterAccountDefinition.decode(characterAccount);
1151
- const currentSystem = starSystemAccountDefinition.decode(systemAccount);
1152
- const currentPlayer = starbasePlayerAccountDefinition.decode(playerAccount);
1153
- if (currentCharacter.playerProfile !== options.authorization.profile ||
1154
- currentCharacter.game !== game) {
1155
- fail(`Character ${character} does not match authorization Profile ${options.authorization.profile} and configured Game ${game}. Refresh the Character and retry deconstruction.`, character);
1156
- }
1157
- if (currentSystem.game !== game ||
1158
- currentSystem.sequenceId !== stake.systemSeqId ||
1159
- currentSystem.starbase === undefined ||
1160
- !currentSystem.celestialBodyAddresses.includes(stake.body.address) ||
1161
- (await deriveStarSystemAddress(game, currentSystem.systemId)) !== system) {
1162
- fail(`Star System ${system} does not match the configured Game, canonical identity, Claim Stake generation, or an active nested Starbase. Refresh the System and retry deconstruction.`, system);
1163
- }
1164
- if (currentPlayer.playerProfile !== options.authorization.profile ||
1165
- currentPlayer.character !== character ||
1166
- currentPlayer.system !== system) {
1167
- fail(`Starbase Player ${player} does not match the authorization Profile, canonical Character, and Claim Stake System. Refresh it and retry deconstruction.`, player);
1168
- }
1266
+ const claimStakeLevel = CLAIM_STAKE_PLOT_LEVELS[row];
1267
+ const [currencyCache, [xpAccrual]] = await Promise.all([
1268
+ claimStakeCurrencyCache(game),
1269
+ deriveStructureXpAccrualAddress(stake.address),
1270
+ ]);
1271
+ const { currentSystem, extraAccounts: [characterCandidate, currencyCandidate, ledgerCandidate], player, playerAccount, systemAccount, } = await readStakeStarbase(ctx, stake, game, system, character, profile, action, [character, currencyCache, xpAccrual]);
1272
+ const { account: characterAccount } = requireProfileCharacter(characterCandidate, character, profile, game, action);
1273
+ const currencyGuard = await requireCurrencyCache(currencyCandidate, currencyCache, game, action, 'Claim Stake deconstruction');
1274
+ // Deconstruction closes an existing XP ledger; a stake without one accrued nothing.
1275
+ const xp = ledgerCandidate === undefined
1276
+ ? {
1277
+ accounts: {},
1278
+ guard: accountAbsentPrecondition(xpAccrual, `Claim Stake ${stake.address} XP ledger ${xpAccrual} remains absent, so no Building XP has accrued.`),
1279
+ }
1280
+ : await resolveClaimStakeXp(ctx, game, stake.address, character);
1169
1281
  const description = `Deconstruct Claim Stake ${stake.address} and discard its bundled hub ${bundledHub.name}; rent settlement, cargo return, account closure, and any refund remain chain-authoritative.`;
1170
- return createPlan({
1282
+ return buildPlannerPlan({
1171
1283
  kind: 'claim-stakes.deconstruct',
1172
- preconditions: await Promise.all([
1173
- precondition(freshGame.account, `The configured Game retains Claim Stake definition ${String(claimDefinition.id)}, bundled hub ${String(bundledHub.id)}, and their current generations.`),
1174
- precondition(stakeAccount, `Claim Stake ${stake.address} retains its ownership, active state, definitions, bundled hub, Body, and System generation.`),
1175
- precondition(bodyAccount, `The Planet retains Claim Stake ${stake.address} at chronology index ${String(claimStakeIndex)} in definition group ${String(claimDefinition.id)}.`),
1176
- precondition(characterAccount, `The canonical Character remains assigned to Profile ${options.authorization.profile} in the configured Game.`),
1177
- precondition(systemAccount, `Star System ${currentSystem.name} retains its configured Game, sequence, nested Starbase, and Claim Stake Body relationship.`),
1178
- precondition(playerAccount, `Starbase Player ${player} retains its Profile, Character, and System.`),
1179
- ]),
1284
+ preconditions: [
1285
+ ...(await Promise.all([
1286
+ accountStatePrecondition(freshGame.account, `The configured Game retains Claim Stake definition ${String(claimDefinition.id)}, bundled hub ${String(bundledHub.id)}, and their current generations.`),
1287
+ accountStatePrecondition(stakeAccount, `Claim Stake ${stake.address} retains its ownership, active state, definitions, bundled hub, Body, and System generation.`),
1288
+ accountStatePrecondition(bodyAccount, `The Planet retains Claim Stake ${stake.address} at chronology index ${String(claimStakeIndex)} in definition group ${String(claimDefinition.id)}.`),
1289
+ characterPrecondition(characterAccount, profile),
1290
+ systemPrecondition(systemAccount, currentSystem.name),
1291
+ playerPrecondition(playerAccount, player),
1292
+ ])),
1293
+ currencyGuard,
1294
+ xp.guard,
1295
+ ...rent.guards,
1296
+ ],
1180
1297
  steps: [
1181
1298
  {
1182
1299
  describes: description,
1183
1300
  instruction: adaptDeconstructClaimStakeInstruction({
1301
+ ...xp.accounts,
1184
1302
  authorization: options.authorization,
1185
1303
  celestialBody: stake.body.address,
1186
1304
  character,
1187
1305
  claimStakeIndex,
1188
1306
  claimStakeInstance: stake.address,
1189
1307
  claimStakeLevel,
1308
+ currencyCache,
1190
1309
  game,
1310
+ ...rent.accounts,
1191
1311
  systemAndStarbasePlayerStarbasePlayer: player,
1192
1312
  systemAndStarbasePlayerSystem: system,
1313
+ xpAccrual,
1193
1314
  }),
1194
1315
  signers: [options.authorization.authority],
1195
1316
  },
@@ -1197,13 +1318,19 @@ export async function planDeconstructClaimStake(ctx, stake, input) {
1197
1318
  summary: description,
1198
1319
  });
1199
1320
  }
1200
- /** Plans one exact raw rent top-up for an owned placed Claim Stake.
1321
+ /** Plans one exact raw rent top-up for an owned Claim Stake.
1201
1322
  *
1323
+ * Active, design and Deactivated (rent-delinquent) stakes all accept a top-up;
1324
+ * topping up a Deactivated stake is how its owner keeps it from eviction.
1202
1325
  * The planner validates the fresh Claim Stake and its canonical
1203
1326
  * Profile/Character/Planet/System/Starbase Player graph, plus the Character's
1204
1327
  * known raw ATLAS balance. Permission checks, concurrent balance changes, rent
1205
1328
  * arithmetic, and duration purchased remain chain-authoritative.
1206
1329
  *
1330
+ * Requires Disabled rent: the Game's rent catalog and the Claim Stake's rent
1331
+ * settlement authority must not exist, and both stay guarded absent until
1332
+ * signing. The instruction carries no rent history.
1333
+ *
1207
1334
  * @example
1208
1335
  * ```ts
1209
1336
  * declare const ctx: SageContext;
@@ -1219,87 +1346,40 @@ export async function planDeconstructClaimStake(ctx, stake, input) {
1219
1346
  export async function planTopUpClaimStakeRent(ctx, stake, input) {
1220
1347
  const options = validateTopUpRentOptions(input);
1221
1348
  const game = requireGameAddress(ctx);
1349
+ const { profile } = options.authorization;
1350
+ // Under Disabled rent a top-up has no lifecycle gate: a Deactivated stake
1351
+ // accepts one, and that is how its owner avoids eviction.
1222
1352
  const loadedState = stake.state;
1223
- if (loadedState.kind === 'deactivated') {
1224
- fail(`Claim Stake ${stake.address} is not placed. Refresh it and choose an active Claim Stake or pending building design before topping up rent.`, stake.address);
1225
- }
1226
- if (stake.game !== game) {
1227
- fail(`Claim Stake ${stake.address} does not match the configured Game ${game}. Refresh it through this context and retry.`, stake.address);
1228
- }
1229
- const character = await findCharacterPda(options.authorization.profile, game);
1230
- if (stake.ownerCharacter.address !== character) {
1231
- fail(`Claim Stake ${stake.address} does not belong to authorization Profile ${options.authorization.profile}'s canonical Character ${character}. Choose an owned Claim Stake, refresh it, and retry.`, stake.address);
1232
- }
1233
- const firstAccounts = await readContextRawAccounts(ctx, [
1234
- stake.address,
1235
- stake.body.address,
1236
- character,
1353
+ const character = await requireOwnedStake(stake, game, profile);
1354
+ const action = 'the rent top-up';
1355
+ const [{ bodyAccount, currentBody, currentStake, extraAccounts: [characterCandidate], stakeAccount, }, rent,] = await Promise.all([
1356
+ readOwnedStake(ctx, stake, action, [character]),
1357
+ requireDisabledStakeRent(ctx, game, stake.address, action),
1237
1358
  ]);
1238
- const stakeAccount = requireAccount(firstAccounts[0], stake.address, `Claim Stake ${stake.address} is missing. Refresh it and retry the rent top-up.`);
1239
- const bodyAccount = requireAccount(firstAccounts[1], stake.body.address, `Celestial Body ${stake.body.address} is missing. Refresh the Claim Stake Body and retry the rent top-up.`);
1240
- const characterAccount = requireAccount(firstAccounts[2], character, `Canonical Character ${character} is missing. Refresh the Profile Character and retry the rent top-up.`);
1241
- const currentStake = claimStakeAccountDefinition.decode(stakeAccount);
1242
- const currentBody = celestialBodyAccountDefinition.decode(bodyAccount);
1243
- const currentCharacter = characterAccountDefinition.decode(characterAccount);
1244
- if (currentStake.game !== game ||
1245
- currentStake.body !== stake.body.address ||
1246
- currentStake.ownerCharacter !== character ||
1247
- currentStake.claimStakeDefinitionId !== stake.claimStakeDefinitionId ||
1248
- currentStake.claimStakeDefinitionSeqId !==
1249
- stake.claimStakeDefinitionSeqId ||
1250
- currentStake.buildingDefinitionSeqId !== stake.buildingDefinitionSeqId ||
1251
- currentStake.systemSeqId !== stake.systemSeqId ||
1359
+ if (!sameOwnedStake(currentStake, stake, game, profile) ||
1252
1360
  currentStake.rentBalanceRaw !== stake.rentBalanceRaw ||
1253
- !sameQuantities(currentStake.buildings, stake.buildings) ||
1254
1361
  !sameStakeState(currentStake.state, loadedState)) {
1255
- fail(`Claim Stake ${stake.address} changed or does not match its loaded ownership, Body, definitions, buildings, rent balance, placed state, or System generation. Refresh it and retry the rent top-up.`, stake.address);
1256
- }
1257
- if (currentBody.kind !== 'planet' ||
1258
- currentBody.game !== game ||
1259
- currentBody.systemSequenceId !== stake.systemSeqId ||
1260
- (await deriveCelestialBodyAddress(game, currentBody.id)) !==
1261
- stake.body.address) {
1262
- fail(`Celestial Body ${stake.body.address} does not match the Claim Stake's configured Game, canonical Planet identity, or System generation. Refresh the Body and Claim Stake before retrying the rent top-up.`, stake.body.address);
1263
- }
1264
- if (currentCharacter.playerProfile !== options.authorization.profile ||
1265
- currentCharacter.game !== game) {
1266
- fail(`Character ${character} does not match authorization Profile ${options.authorization.profile} and configured Game ${game}. Refresh the Character and retry the rent top-up.`, character);
1362
+ fail(`Claim Stake ${stake.address} changed or does not match its loaded ownership, Body, definitions, buildings, rent balance, lifecycle state, or System generation. Refresh it and retry the rent top-up.`, stake.address);
1267
1363
  }
1364
+ const { system } = await requireStakePlanet(currentBody, stake, game, action);
1365
+ const { account: characterAccount, current: currentCharacter } = requireProfileCharacter(characterCandidate, character, profile, game, action);
1268
1366
  if (currentCharacter.atlasRaw < options.amount) {
1269
1367
  fail(`Character ${character} has only ${String(currentCharacter.atlasRaw)} known raw ATLAS, below the requested ${String(options.amount)} raw rent top-up. Reduce the amount or add ATLAS, refresh the Character, and retry.`, character);
1270
1368
  }
1271
- const system = currentBody.system;
1272
- const player = await deriveStarbasePlayerAddress(system, character);
1273
- const [systemCandidate, playerCandidate] = await readContextRawAccounts(ctx, [
1274
- system,
1275
- player,
1276
- ]);
1277
- const systemAccount = requireAccount(systemCandidate, system, `Star System ${system} is missing. Refresh the Claim Stake's System and retry the rent top-up.`);
1278
- const playerAccount = requireAccount(playerCandidate, player, `Starbase Player ${player} is missing for authorization Profile ${options.authorization.profile}. Register or refresh it in the Claim Stake's System before retrying the rent top-up.`);
1279
- const currentSystem = starSystemAccountDefinition.decode(systemAccount);
1280
- const currentPlayer = starbasePlayerAccountDefinition.decode(playerAccount);
1281
- if (currentSystem.game !== game ||
1282
- currentSystem.sequenceId !== stake.systemSeqId ||
1283
- currentSystem.starbase === undefined ||
1284
- !currentSystem.celestialBodyAddresses.includes(stake.body.address) ||
1285
- (await deriveStarSystemAddress(game, currentSystem.systemId)) !== system) {
1286
- fail(`Star System ${system} does not match the configured Game, canonical identity, Claim Stake generation, active nested Starbase, or Claim Stake Body relationship. Refresh the System and retry the rent top-up.`, system);
1287
- }
1288
- if (currentPlayer.playerProfile !== options.authorization.profile ||
1289
- currentPlayer.character !== character ||
1290
- currentPlayer.system !== system) {
1291
- fail(`Starbase Player ${player} does not match the authorization Profile, canonical Character, and Claim Stake System. Refresh it and retry the rent top-up.`, player);
1292
- }
1369
+ const { currentSystem, player, playerAccount, systemAccount } = await readStakeStarbase(ctx, stake, game, system, character, profile, action);
1293
1370
  const description = `Top up Claim Stake ${stake.address} rent with ${String(options.amount)} raw rent units; rent arithmetic remains chain-authoritative.`;
1294
- return createPlan({
1371
+ return buildPlannerPlan({
1295
1372
  kind: 'claim-stakes.top-up-rent',
1296
- preconditions: await Promise.all([
1297
- precondition(stakeAccount, `Claim Stake ${stake.address} retains its ownership, rent balance, placed state, Body, and System generation.`),
1298
- precondition(bodyAccount, `The Claim Stake Planet remains assigned to System ${system} at the current generation.`),
1299
- precondition(characterAccount, `The canonical Character remains assigned to Profile ${options.authorization.profile} with at least ${String(options.amount)} known raw ATLAS.`),
1300
- precondition(systemAccount, `Star System ${currentSystem.name} retains its configured Game, sequence, nested Starbase, and Claim Stake Body relationship.`),
1301
- precondition(playerAccount, `Starbase Player ${player} retains its Profile, Character, and System.`),
1302
- ]),
1373
+ preconditions: [
1374
+ ...(await Promise.all([
1375
+ accountStatePrecondition(stakeAccount, `Claim Stake ${stake.address} retains its ownership, rent balance, lifecycle state, Body, and System generation.`),
1376
+ planetPrecondition(bodyAccount, system),
1377
+ accountStatePrecondition(characterAccount, `The canonical Character remains assigned to Profile ${profile} with at least ${String(options.amount)} known raw ATLAS.`),
1378
+ systemPrecondition(systemAccount, currentSystem.name),
1379
+ playerPrecondition(playerAccount, player),
1380
+ ])),
1381
+ ...rent.guards,
1382
+ ],
1303
1383
  steps: [
1304
1384
  {
1305
1385
  describes: description,
@@ -1310,6 +1390,7 @@ export async function planTopUpClaimStakeRent(ctx, stake, input) {
1310
1390
  character,
1311
1391
  claimStakeInstance: stake.address,
1312
1392
  game,
1393
+ ...rent.accounts,
1313
1394
  systemAndStarbasePlayerStarbasePlayer: player,
1314
1395
  systemAndStarbasePlayerSystem: system,
1315
1396
  }),
@@ -1341,10 +1422,10 @@ function requireTransferCapacity(destination, address, used, requested, total) {
1341
1422
  fail(`${destination} has ${String(remaining)} raw capacity units remaining, but this transfer requires ${String(requested)}. Reduce the request or free capacity, refresh both snapshots, and retry.`, address);
1342
1423
  }
1343
1424
  }
1344
- function fleetCargoAmount(fleet, cargoId) {
1345
- return ((fleet.ammoBank.cargoId === cargoId ? fleet.ammoBank.amount : 0n) +
1346
- (fleet.fuelTank.cargoId === cargoId ? fleet.fuelTank.amount : 0n) +
1347
- cargoAmount(fleet.cargoHold.amounts, cargoId));
1425
+ /** Reads a Fleet's cargo-hold balance; Claim Stake transfers never touch the
1426
+ * fuel tank or ammo bank, so Fuel and Ammunition count only when in the hold. */
1427
+ function fleetCargoHoldAmount(fleet, cargoId) {
1428
+ return cargoAmount(fleet.cargoHold.amounts, cargoId);
1348
1429
  }
1349
1430
  function describeCargoRequests(direction, requests) {
1350
1431
  return requests.map(({ amount, cargoId }) => `${direction} cargo id ${String(cargoId)} = ${String(amount)} raw ${amount === 1n ? 'unit' : 'units'}`);
@@ -1354,8 +1435,24 @@ function describeCargoRequests(direction, requests) {
1354
1435
  * The planner validates the shared Profile/Game, idle Fleet with normalized
1355
1436
  * crew, active Claim Stake, canonical Planet/System/Starbase Player graph,
1356
1437
  * exact source balances, exact destination capacities, and an optional present
1357
- * FleetCrewBinding. The chain remains authoritative for range, duration,
1358
- * permission bits, concurrent state changes, and transfer arithmetic.
1438
+ * FleetCrewBinding. Both legs use the Fleet cargo hold only: loaded Fuel and
1439
+ * Ammunition arrive in the cargo hold and consume cargo capacity, and unloads
1440
+ * are sourced from the cargo hold, never from the fuel tank or ammo bank. The
1441
+ * chain remains authoritative for range, duration, permission bits, concurrent
1442
+ * state changes, and transfer arithmetic.
1443
+ *
1444
+ * The Fleet stores the requested load and unload lines while the transfer
1445
+ * runs. The program settles the Fleet and stake together: the stake's lamports
1446
+ * above its rent floor fund the Fleet first and the Profile vault, with the
1447
+ * signing key, funds the rest. The Plan bounds that debit with the same key and
1448
+ * balance checks as placement.
1449
+ *
1450
+ * Requires Game 2 and its existing canonical Claim Stake XP ledger.
1451
+ * Missing XP state must be repaired outside this Plan; no accounts are created.
1452
+ *
1453
+ * Requires Disabled rent: the Game's rent catalog and the Claim Stake's rent
1454
+ * settlement authority must not exist, and both stay guarded absent until
1455
+ * signing. The instruction carries no rent history.
1359
1456
  *
1360
1457
  * @example
1361
1458
  * ```ts
@@ -1374,119 +1471,56 @@ function describeCargoRequests(direction, requests) {
1374
1471
  export async function planStartClaimStakeFleetTransfer(ctx, fleet, stake, input) {
1375
1472
  const options = validateStartFleetTransferOptions(input);
1376
1473
  const game = requireGameAddress(ctx);
1474
+ const { profile } = options.authorization;
1377
1475
  if (fleet.game !== game || stake.game !== game) {
1378
1476
  fail(`Fleet ${fleet.address} and Claim Stake ${stake.address} must both belong to configured Game ${game}. Refresh both snapshots through this context and retry.`, fleet.game !== game ? fleet.address : stake.address);
1379
1477
  }
1380
- if (fleet.ownerProfile.address !== options.authorization.profile) {
1381
- fail(`Fleet ${fleet.name} belongs to Profile ${fleet.ownerProfile.address}, not authorization Profile ${options.authorization.profile}. Use the owning Profile, refresh the Fleet, and retry.`, fleet.address);
1478
+ if (fleet.ownerProfile.address !== profile) {
1479
+ fail(`Fleet ${fleet.name} belongs to Profile ${fleet.ownerProfile.address}, not authorization Profile ${profile}. Use the owning Profile, refresh the Fleet, and retry.`, fleet.address);
1382
1480
  }
1383
1481
  if (fleet.state.kind !== 'idle') {
1384
1482
  fail(`Fleet ${fleet.name} is ${fleet.state.kind}, but starting a Claim Stake transfer requires an idle Fleet. Return it to idle state, refresh it, and retry.`, fleet.address);
1385
1483
  }
1484
+ const crewFailure = `Fleet ${fleet.name} has no valid normalized crew and cannot start a Claim Stake transfer. Crew the Fleet, refresh it, and retry.`;
1386
1485
  if (!Number.isSafeInteger(fleet.crewCount) || fleet.crewCount <= 0) {
1387
- fail(`Fleet ${fleet.name} has no valid normalized crew and cannot start a Claim Stake transfer. Crew the Fleet, refresh it, and retry.`, fleet.address);
1486
+ fail(crewFailure, fleet.address);
1388
1487
  }
1389
1488
  if (stake.state.kind !== 'active') {
1390
1489
  fail(`Claim Stake ${stake.address} is ${stake.state.kind}, but starting a Fleet transfer requires an active Claim Stake. Activate or restore it, refresh it, and retry.`, stake.address);
1391
1490
  }
1392
- const character = await findCharacterPda(options.authorization.profile, game);
1393
- if (stake.ownerCharacter.address !== character) {
1394
- fail(`Claim Stake ${stake.address} does not belong to authorization Profile ${options.authorization.profile}'s canonical Character ${character}. Choose an owned active Claim Stake, refresh it, and retry.`, stake.address);
1395
- }
1396
- const [fleetCandidate, stakeCandidate, bodyCandidate, characterCandidate] = await readContextRawAccounts(ctx, [
1397
- fleet.address,
1398
- stake.address,
1399
- stake.body.address,
1400
- character,
1491
+ const character = await requireOwnedStake(stake, game, profile);
1492
+ const action = 'the Fleet transfer';
1493
+ const [{ bodyAccount, currentBody, currentStake, extraAccounts: [fleetCandidate, characterCandidate], stakeAccount, }, rent,] = await Promise.all([
1494
+ readOwnedStake(ctx, stake, action, [fleet.address, character]),
1495
+ requireDisabledStakeRent(ctx, game, stake.address, action),
1401
1496
  ]);
1402
- const fleetAccount = requireAccount(fleetCandidate, fleet.address, `Fleet ${fleet.name} is missing. Refresh it and retry the Claim Stake transfer.`);
1403
- const stakeAccount = requireAccount(stakeCandidate, stake.address, `Claim Stake ${stake.address} is missing. Refresh it and retry the Fleet transfer.`);
1404
- const bodyAccount = requireAccount(bodyCandidate, stake.body.address, `Celestial Body ${stake.body.address} is missing. Refresh the Claim Stake Body and retry the Fleet transfer.`);
1405
- const characterAccount = requireAccount(characterCandidate, character, `Canonical Character ${character} is missing. Refresh the Profile Character and retry the Claim Stake transfer.`);
1497
+ const fleetAccount = requireAccount(fleetCandidate, fleet.address, `Fleet ${fleet.name} is missing. Refresh it and retry the Fleet transfer.`);
1406
1498
  const currentFleet = fleetAccountDefinition.decode(fleetAccount);
1407
- const currentStake = claimStakeAccountDefinition.decode(stakeAccount);
1408
- const currentBody = celestialBodyAccountDefinition.decode(bodyAccount);
1409
- const currentCharacter = characterAccountDefinition.decode(characterAccount);
1410
1499
  if (!Number.isSafeInteger(currentFleet.crewCount) ||
1411
1500
  currentFleet.crewCount <= 0) {
1412
- fail(`Fleet ${fleet.name} has no valid normalized crew and cannot start a Claim Stake transfer. Crew the Fleet, refresh it, and retry.`, fleet.address);
1501
+ fail(crewFailure, fleet.address);
1413
1502
  }
1414
- if (currentFleet.game !== game ||
1415
- currentFleet.ownerProfile !== options.authorization.profile ||
1416
- currentFleet.name !== fleet.name ||
1503
+ if (!sameTransferFleet(currentFleet, fleet, game, profile) ||
1417
1504
  currentFleet.state.kind !== 'idle' ||
1418
1505
  currentFleet.crewCount !== fleet.crewCount ||
1419
- (await deriveFleetAddress(currentFleet)) !== fleet.address ||
1420
- currentFleet.ammoBank.cargoId !== fleet.ammo.id ||
1421
- currentFleet.ammoBank.amount !== fleet.ammo.amount ||
1422
- currentFleet.fuelTank.cargoId !== fleet.fuel.id ||
1423
- currentFleet.fuelTank.amount !== fleet.fuel.amount ||
1424
- currentFleet.cargoHold.storageCost !== fleet.cargoHold.storageCost ||
1425
- !sameCargoAmounts(currentFleet.cargoHold.amounts, fleet.cargoHold.items) ||
1426
- currentFleet.stats.cargo.ammoCapacity !== fleet.capacities.ammo.total ||
1427
- currentFleet.stats.cargo.fuelCapacity !== fleet.capacities.fuel.total ||
1428
- currentFleet.stats.cargo.cargoCapacity !== fleet.capacities.cargo.total) {
1506
+ (await deriveFleetAddress(currentFleet)) !== fleet.address) {
1429
1507
  fail(`Fleet ${fleet.name} changed or no longer matches its loaded Game, owner, canonical address, name, idle state, normalized crew, cargo, or capacities. Refresh it and create a new Claim Stake transfer Plan.`, fleet.address);
1430
1508
  }
1431
- if (currentStake.game !== game ||
1432
- currentStake.ownerCharacter !== character ||
1433
- currentStake.body !== stake.body.address ||
1434
- currentStake.systemSeqId !== stake.systemSeqId ||
1435
- currentStake.state.kind !== 'active' ||
1436
- currentStake.production.cargoPod.storageCost !==
1437
- stake.resources.capacity.usedRaw ||
1438
- currentStake.production.resourceCapacityRaw !==
1439
- stake.resources.capacity.totalRaw ||
1440
- !sameCargoAmounts(currentStake.production.cargoPod.amounts, stake.resources.inventory.items.map(({ id, quantityRaw }) => ({
1441
- amount: quantityRaw,
1442
- id,
1443
- })))) {
1509
+ if (!sameTransferStake(currentStake, stake, game, profile)) {
1444
1510
  fail(`Claim Stake ${stake.address} changed or no longer matches its loaded Game, owner, Body, active state, resources, capacity, or System generation. Refresh it and create a new Fleet transfer Plan.`, stake.address);
1445
1511
  }
1446
- if (currentBody.kind !== 'planet' ||
1447
- currentBody.game !== game ||
1448
- currentBody.systemSequenceId !== stake.systemSeqId ||
1449
- (await deriveCelestialBodyAddress(game, currentBody.id)) !==
1450
- stake.body.address) {
1451
- fail(`Celestial Body ${stake.body.address} does not match the Claim Stake's configured Game, canonical Planet identity, or System generation. Refresh the Body and Claim Stake before retrying the Fleet transfer.`, stake.body.address);
1452
- }
1453
- if (currentCharacter.playerProfile !== options.authorization.profile ||
1454
- currentCharacter.game !== game) {
1455
- fail(`Character ${character} does not match authorization Profile ${options.authorization.profile} and configured Game ${game}. Refresh the Character and retry the Claim Stake transfer.`, character);
1456
- }
1457
- const system = currentBody.system;
1458
- const player = await deriveStarbasePlayerAddress(system, character);
1459
- const currencyCache = await deriveCurrencyCacheAddress(game);
1460
- const crewBinding = await deriveFleetCrewBindingAddress(fleet.address);
1461
- const [systemCandidate, playerCandidate, currencyCandidate, crewCandidate] = await readContextRawAccounts(ctx, [
1462
- system,
1463
- player,
1464
- currencyCache,
1465
- crewBinding,
1512
+ const { system } = await requireStakePlanet(currentBody, stake, game, action);
1513
+ const { account: characterAccount } = requireProfileCharacter(characterCandidate, character, profile, game, action);
1514
+ const [currencyCache, crewBinding] = await Promise.all([
1515
+ claimStakeCurrencyCache(game),
1516
+ deriveFleetCrewBindingAddress(fleet.address),
1466
1517
  ]);
1467
- const systemAccount = requireAccount(systemCandidate, system, `Star System ${system} is missing. Refresh the Claim Stake System and retry the Fleet transfer.`);
1468
- const playerAccount = requireAccount(playerCandidate, player, `Starbase Player ${player} is missing for authorization Profile ${options.authorization.profile}. Register or refresh it in the Claim Stake System before retrying the Fleet transfer.`);
1469
- const currencyAccount = requireAccount(currencyCandidate, currencyCache, `Canonical CurrencyCache ${currencyCache} is missing for configured Game ${game}. Refresh the Game currency configuration and retry the Fleet transfer.`);
1470
- const currentSystem = starSystemAccountDefinition.decode(systemAccount);
1471
- const currentPlayer = starbasePlayerAccountDefinition.decode(playerAccount);
1472
- if (currentSystem.game !== game ||
1473
- currentSystem.sequenceId !== stake.systemSeqId ||
1474
- currentSystem.starbase === undefined ||
1475
- !currentSystem.celestialBodyAddresses.includes(stake.body.address) ||
1476
- (await deriveStarSystemAddress(game, currentSystem.systemId)) !== system) {
1477
- fail(`Star System ${system} does not match the configured Game, canonical identity, Claim Stake generation, nested Starbase, or Claim Stake Body relationship. Refresh the System and retry the Fleet transfer.`, system);
1478
- }
1479
- if (currentPlayer.playerProfile !== options.authorization.profile ||
1480
- currentPlayer.character !== character ||
1481
- currentPlayer.system !== system) {
1482
- fail(`Starbase Player ${player} does not match the authorization Profile, canonical Character, and Claim Stake System. Refresh it and retry the Fleet transfer.`, player);
1483
- }
1484
- validateCurrencyCache(currencyAccount, game, currencyCache, 'the Claim Stake Fleet transfer');
1518
+ const { currentSystem, extraAccounts: [currencyCandidate, crewCandidate], player, playerAccount, systemAccount, } = await readStakeStarbase(ctx, stake, game, system, character, profile, action, [currencyCache, crewBinding]);
1519
+ const currencyGuard = await requireCurrencyCache(currencyCandidate, currencyCache, game, action, 'Claim Stake Fleet transfer');
1485
1520
  if (crewCandidate !== undefined) {
1486
1521
  const currentCrewBinding = fleetCrewBindingAccountDefinition.decode(crewCandidate);
1487
1522
  if (crewCandidate.address !== crewBinding ||
1488
- currentCrewBinding.fleet !== fleet.address ||
1489
- currentCrewBinding.ownerProfile !== options.authorization.profile) {
1523
+ !matchesFleetCrewBindingIdentity(currentCrewBinding, fleet.address, profile)) {
1490
1524
  fail(`FleetCrewBinding ${crewCandidate.address} does not match Fleet ${fleet.address}, its owner Profile, or its canonical PDA. Refresh the Fleet crew registration or remove the invalid binding before retrying.`, crewCandidate.address);
1491
1525
  }
1492
1526
  }
@@ -1498,64 +1532,63 @@ export async function planStartClaimStakeFleetTransfer(ctx, fleet, stake, input)
1498
1532
  requireCargoBalance(`Claim Stake ${stake.address}`, stake.address, request.cargoId, cargoAmount(currentStake.production.cargoPod.amounts, request.cargoId), request.amount);
1499
1533
  }
1500
1534
  for (const request of options.unload) {
1501
- requireCargoBalance(`Fleet ${fleet.name}`, fleet.address, request.cargoId, fleetCargoAmount(currentFleet, request.cargoId), request.amount);
1502
- }
1503
- const loadAmmo = options.load
1504
- .filter(({ cargoId }) => cargoId === currentFleet.ammoBank.cargoId)
1505
- .reduce((total, { amount }) => total + amount, 0n);
1506
- const loadFuel = options.load
1507
- .filter(({ cargoId }) => cargoId === currentFleet.fuelTank.cargoId)
1508
- .reduce((total, { amount }) => total + amount, 0n);
1509
- const loadCargoStorage = options.load
1510
- .filter(({ cargoId }) => cargoId !== currentFleet.ammoBank.cargoId &&
1511
- cargoId !== currentFleet.fuelTank.cargoId)
1512
- .reduce((total, { amount, cargoId }) => total + amount * BigInt(definitions.get(cargoId).storageCost), 0n);
1513
- requireTransferCapacity(`Fleet ${fleet.name} ammo destination`, fleet.address, currentFleet.ammoBank.amount, loadAmmo, currentFleet.stats.cargo.ammoCapacity);
1514
- requireTransferCapacity(`Fleet ${fleet.name} fuel destination`, fleet.address, currentFleet.fuelTank.amount, loadFuel, currentFleet.stats.cargo.fuelCapacity);
1535
+ requireCargoBalance(`Fleet ${fleet.name}`, fleet.address, request.cargoId, fleetCargoHoldAmount(currentFleet, request.cargoId), request.amount);
1536
+ }
1537
+ // Every load, including Fuel and Ammunition, lands in the cargo hold.
1538
+ const loadCargoStorage = options.load.reduce((total, { amount, cargoId }) => total + cargoStorageUnits(amount, definitions.get(cargoId).storageCost), 0n);
1515
1539
  requireTransferCapacity(`Fleet ${fleet.name} cargo destination`, fleet.address, currentFleet.cargoHold.storageCost, loadCargoStorage, currentFleet.stats.cargo.cargoCapacity);
1516
- const unloadStorage = options.unload.reduce((total, { amount, cargoId }) => total + amount * BigInt(definitions.get(cargoId).storageCost), 0n);
1540
+ const unloadStorage = options.unload.reduce((total, { amount, cargoId }) => total + cargoStorageUnits(amount, definitions.get(cargoId).storageCost), 0n);
1517
1541
  requireTransferCapacity(`Claim Stake ${stake.address} resource destination`, stake.address, currentStake.production.cargoPod.storageCost, unloadStorage, currentStake.production.resourceCapacityRaw);
1518
1542
  const descriptions = [
1519
1543
  ...describeCargoRequests('load', options.load),
1520
1544
  ...describeCargoRequests('unload', options.unload),
1521
1545
  ];
1522
- const description = `Start fleet ${fleet.name}'s transfer to Claim Stake ${stake.address}: ${descriptions.join('; ')}. Range and transfer duration remain chain-authoritative.`;
1523
- return createPlan({
1546
+ const xp = await resolveClaimStakeXp(ctx, game, stake.address);
1547
+ const instruction = adaptStartClaimStakeFleetTransferInstruction({
1548
+ ...xp.accounts,
1549
+ authorization: options.authorization,
1550
+ celestialBody: stake.body.address,
1551
+ character,
1552
+ claimStakeInstance: stake.address,
1553
+ ...(crewCandidate === undefined ? {} : { crewBinding }),
1554
+ currencyCache,
1555
+ fleet: fleet.address,
1556
+ game,
1557
+ ...rent.accounts,
1558
+ systemAndStarbasePlayerStarbasePlayer: player,
1559
+ systemAndStarbasePlayerSystem: system,
1560
+ toLoad: options.load.map(({ amount, cargoId }) => [cargoId, amount]),
1561
+ toUnload: options.unload.map(({ amount, cargoId }) => [cargoId, amount]),
1562
+ });
1563
+ // The Fleet's transfer state is funded first from the stake's surplus over
1564
+ // its rent floor, then from the Profile vault.
1565
+ const storage = await planClaimStakeStorage(ctx, instruction, profile, [fleetAccount, stakeAccount], 'Claim Stake Fleet transfer start');
1566
+ const description = `Start fleet ${fleet.name}'s transfer to Claim Stake ${stake.address}: ${descriptions.join('; ')}. Loaded cargo, including Fuel and Ammunition, arrives in the Fleet cargo hold; unloads come from the cargo hold only. Range and transfer duration remain chain-authoritative. ${storage.disclosure}`;
1567
+ return buildPlannerPlan({
1524
1568
  kind: 'claim-stakes.start-fleet-transfer',
1525
- preconditions: await Promise.all([
1526
- precondition(fleetAccount, `Fleet ${fleet.name} retains its owner, name, idle state, normalized crew, cargo balances, and destination capacities.`),
1527
- precondition(stakeAccount, `Claim Stake ${stake.address} retains its owner, active state, cargo balances, destination capacity, Body, and System generation.`),
1528
- precondition(bodyAccount, `The Claim Stake Planet remains assigned to System ${system} at the current generation.`),
1529
- precondition(characterAccount, `The canonical Character remains assigned to Profile ${options.authorization.profile} in the configured Game.`),
1530
- precondition(systemAccount, `Star System ${currentSystem.name} retains its configured Game, sequence, nested Starbase, and Claim Stake Body relationship.`),
1531
- precondition(playerAccount, `Starbase Player ${player} retains its Profile, Character, and System.`),
1532
- precondition(currencyAccount, 'The canonical CurrencyCache remains assigned to the configured Game.'),
1533
- ...(crewCandidate === undefined
1534
- ? []
1535
- : [
1536
- precondition(crewCandidate, `Fleet ${fleet.name}'s optional FleetCrewBinding remains assigned to the Fleet and owner Profile.`),
1537
- ]),
1538
- ]),
1569
+ preconditions: [
1570
+ xp.guard,
1571
+ ...(await Promise.all([
1572
+ accountStatePrecondition(fleetAccount, `Fleet ${fleet.name} retains its owner, name, idle state, normalized crew, cargo balances, and destination capacities.`),
1573
+ accountStatePrecondition(stakeAccount, `Claim Stake ${stake.address} retains its owner, active state, cargo balances, destination capacity, Body, and System generation.`),
1574
+ planetPrecondition(bodyAccount, system),
1575
+ characterPrecondition(characterAccount, profile),
1576
+ systemPrecondition(systemAccount, currentSystem.name),
1577
+ playerPrecondition(playerAccount, player),
1578
+ ...(crewCandidate === undefined
1579
+ ? []
1580
+ : [
1581
+ accountStatePrecondition(crewCandidate, `Fleet ${fleet.name}'s optional FleetCrewBinding remains assigned to the Fleet and owner Profile.`),
1582
+ ]),
1583
+ ])),
1584
+ currencyGuard,
1585
+ ...rent.guards,
1586
+ storage.guard,
1587
+ ],
1539
1588
  steps: [
1540
1589
  {
1541
1590
  describes: description,
1542
- instruction: adaptStartClaimStakeFleetTransferInstruction({
1543
- authorization: options.authorization,
1544
- celestialBody: stake.body.address,
1545
- character,
1546
- claimStakeInstance: stake.address,
1547
- ...(crewCandidate === undefined ? {} : { crewBinding }),
1548
- currencyCache,
1549
- fleet: fleet.address,
1550
- game,
1551
- systemAndStarbasePlayerStarbasePlayer: player,
1552
- systemAndStarbasePlayerSystem: system,
1553
- toLoad: options.load.map(({ amount, cargoId }) => [cargoId, amount]),
1554
- toUnload: options.unload.map(({ amount, cargoId }) => [
1555
- cargoId,
1556
- amount,
1557
- ]),
1558
- }),
1591
+ instruction,
1559
1592
  signers: [options.authorization.authority],
1560
1593
  },
1561
1594
  ],
@@ -1606,10 +1639,7 @@ export async function planRecoverClaimStakeFleetTransfer(ctx, fleet, stake, inpu
1606
1639
  if (options.atUnixSeconds < fleet.state.endAtUnixSeconds) {
1607
1640
  fail(`Fleet ${fleet.name}'s Claim Stake transfer lock has not elapsed at Unix timestamp ${String(options.atUnixSeconds)}; it ends at ${String(fleet.state.endAtUnixSeconds)}. Wait until the lock elapses, provide a current timestamp, and retry recovery.`, fleet.address);
1608
1641
  }
1609
- const character = await findCharacterPda(options.authorization.profile, game);
1610
- if (stake.ownerCharacter.address !== character) {
1611
- fail(`Claim Stake ${stake.address} does not belong to authorization Profile ${options.authorization.profile}'s canonical Character ${character}. Supply the owned current Claim Stake and retry recovery.`, stake.address);
1612
- }
1642
+ const character = await requireOwnedStake(stake, game, options.authorization.profile);
1613
1643
  const [fleetCandidate, stakeCandidate] = await readContextRawAccounts(ctx, [
1614
1644
  fleet.address,
1615
1645
  stake.address,
@@ -1618,40 +1648,17 @@ export async function planRecoverClaimStakeFleetTransfer(ctx, fleet, stake, inpu
1618
1648
  const stakeAccount = requireAccount(stakeCandidate, stake.address, `Claim Stake ${stake.address} is missing. Refresh it and retry Fleet transfer recovery.`);
1619
1649
  const currentFleet = fleetAccountDefinition.decode(fleetAccount);
1620
1650
  const currentStake = claimStakeAccountDefinition.decode(stakeAccount);
1621
- if (currentFleet.game !== game ||
1622
- currentFleet.ownerProfile !== options.authorization.profile ||
1623
- currentFleet.name !== fleet.name ||
1651
+ if (!sameTransferFleet(currentFleet, fleet, game, options.authorization.profile) ||
1624
1652
  currentFleet.state.kind !== 'claimStakeTransfer' ||
1625
1653
  currentFleet.state.claimStake !== stake.address ||
1626
1654
  currentFleet.state.planet !== stake.body.address ||
1627
1655
  currentFleet.state.endAtUnixSeconds !== fleet.state.endAtUnixSeconds ||
1628
1656
  !sameCargoAmounts(currentFleet.state.load, fleet.state.load) ||
1629
1657
  !sameCargoAmounts(currentFleet.state.unload, fleet.state.unload) ||
1630
- (await deriveFleetAddress(currentFleet)) !== fleet.address ||
1631
- currentFleet.ammoBank.cargoId !== fleet.ammo.id ||
1632
- currentFleet.ammoBank.amount !== fleet.ammo.amount ||
1633
- currentFleet.fuelTank.cargoId !== fleet.fuel.id ||
1634
- currentFleet.fuelTank.amount !== fleet.fuel.amount ||
1635
- currentFleet.cargoHold.storageCost !== fleet.cargoHold.storageCost ||
1636
- !sameCargoAmounts(currentFleet.cargoHold.amounts, fleet.cargoHold.items) ||
1637
- currentFleet.stats.cargo.ammoCapacity !== fleet.capacities.ammo.total ||
1638
- currentFleet.stats.cargo.fuelCapacity !== fleet.capacities.fuel.total ||
1639
- currentFleet.stats.cargo.cargoCapacity !== fleet.capacities.cargo.total) {
1658
+ (await deriveFleetAddress(currentFleet)) !== fleet.address) {
1640
1659
  fail(`Fleet ${fleet.name} changed or no longer matches its loaded owner, Game, canonical address, Claim Stake transfer snapshot, cargo, or capacities. Refresh it and retry recovery.`, fleet.address);
1641
1660
  }
1642
- if (currentStake.game !== game ||
1643
- currentStake.ownerCharacter !== character ||
1644
- currentStake.body !== stake.body.address ||
1645
- currentStake.systemSeqId !== stake.systemSeqId ||
1646
- currentStake.state.kind !== 'active' ||
1647
- currentStake.production.cargoPod.storageCost !==
1648
- stake.resources.capacity.usedRaw ||
1649
- currentStake.production.resourceCapacityRaw !==
1650
- stake.resources.capacity.totalRaw ||
1651
- !sameCargoAmounts(currentStake.production.cargoPod.amounts, stake.resources.inventory.items.map(({ id, quantityRaw }) => ({
1652
- amount: quantityRaw,
1653
- id,
1654
- })))) {
1661
+ if (!sameTransferStake(currentStake, stake, game, options.authorization.profile)) {
1655
1662
  fail(`Claim Stake ${stake.address} changed or no longer matches its loaded owner, Game, Body, active state, resources, capacity, or System generation. Refresh it and retry Fleet transfer recovery.`, stake.address);
1656
1663
  }
1657
1664
  let reason;
@@ -1664,7 +1671,7 @@ export async function planRecoverClaimStakeFleetTransfer(ctx, fleet, stake, inpu
1664
1671
  }
1665
1672
  if (reason === undefined) {
1666
1673
  for (const request of currentFleet.state.unload) {
1667
- const available = fleetCargoAmount(currentFleet, request.cargoId);
1674
+ const available = fleetCargoHoldAmount(currentFleet, request.cargoId);
1668
1675
  if (available < request.amount) {
1669
1676
  reason = `Fleet source shortfall for cargo id ${String(request.cargoId)}`;
1670
1677
  break;
@@ -1690,22 +1697,12 @@ export async function planRecoverClaimStakeFleetTransfer(ctx, fleet, stake, inpu
1690
1697
  }
1691
1698
  }
1692
1699
  if (reason === undefined) {
1693
- const loadAmmo = cargoAmount(currentFleet.state.load, currentFleet.ammoBank.cargoId);
1694
- const loadFuel = cargoAmount(currentFleet.state.load, currentFleet.fuelTank.cargoId);
1695
- const loadCargoStorage = currentFleet.state.load
1696
- .filter(({ cargoId }) => cargoId !== currentFleet.ammoBank.cargoId &&
1697
- cargoId !== currentFleet.fuelTank.cargoId)
1698
- .reduce((total, { amount, cargoId }) => total + amount * BigInt(queuedDefinitions.get(cargoId).storageCost), 0n);
1699
- const unloadStorage = currentFleet.state.unload.reduce((total, { amount, cargoId }) => total + amount * BigInt(queuedDefinitions.get(cargoId).storageCost), 0n);
1700
- if (currentFleet.ammoBank.amount + loadAmmo >
1701
- currentFleet.stats.cargo.ammoCapacity) {
1702
- reason = 'Fleet ammo destination overflow';
1703
- }
1704
- else if (currentFleet.fuelTank.amount + loadFuel >
1705
- currentFleet.stats.cargo.fuelCapacity) {
1706
- reason = 'Fleet fuel destination overflow';
1707
- }
1708
- else if (currentFleet.cargoHold.storageCost + loadCargoStorage >
1700
+ // Every queued load, including Fuel and Ammunition, targets the cargo hold.
1701
+ const loadCargoStorage = currentFleet.state.load.reduce((total, { amount, cargoId }) => total +
1702
+ cargoStorageUnits(amount, queuedDefinitions.get(cargoId).storageCost), 0n);
1703
+ const unloadStorage = currentFleet.state.unload.reduce((total, { amount, cargoId }) => total +
1704
+ cargoStorageUnits(amount, queuedDefinitions.get(cargoId).storageCost), 0n);
1705
+ if (currentFleet.cargoHold.storageCost + loadCargoStorage >
1709
1706
  currentFleet.stats.cargo.cargoCapacity) {
1710
1707
  reason = 'Fleet cargo destination overflow';
1711
1708
  }
@@ -1718,15 +1715,15 @@ export async function planRecoverClaimStakeFleetTransfer(ctx, fleet, stake, inpu
1718
1715
  fail(`Fleet ${fleet.name}'s Claim Stake transfer is currently executable. Use the permissionless normal exit path; recovery is only for a locally proven non-executable transfer.`, fleet.address);
1719
1716
  }
1720
1717
  const description = `Recover fleet ${fleet.name} from its stuck Claim Stake transfer (${reason}); no cargo moves and the Fleet returns to idle.`;
1721
- return createPlan({
1718
+ return buildPlannerPlan({
1722
1719
  kind: 'claim-stakes.recover-fleet-transfer',
1723
1720
  preconditions: await Promise.all([
1724
- precondition(fleetAccount, `Fleet ${fleet.name} retains its owner and exact elapsed, non-executable Claim Stake transfer snapshot.`),
1725
- precondition(stakeAccount, `Claim Stake ${stake.address} remains active with the current owner, cargo, and capacity used to prove non-executability.`),
1721
+ accountStatePrecondition(fleetAccount, `Fleet ${fleet.name} retains its owner and exact elapsed, non-executable Claim Stake transfer snapshot.`),
1722
+ accountStatePrecondition(stakeAccount, `Claim Stake ${stake.address} remains active with the current owner, cargo, and capacity used to prove non-executability.`),
1726
1723
  ...(gameAccount === undefined
1727
1724
  ? []
1728
1725
  : [
1729
- precondition(gameAccount, `Game ${game} retains the cargo definitions used to prove the transfer is non-executable.`),
1726
+ accountStatePrecondition(gameAccount, `Game ${game} retains the cargo definitions used to prove the transfer is non-executable.`),
1730
1727
  ]),
1731
1728
  ]),
1732
1729
  steps: [