@sly-rentals/core 5.5.2 → 6.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/dist/api.json +3556 -3373
  2. package/dist/cjs/doctor/contract.js +106 -66
  3. package/dist/cjs/doctor/contract.js.map +1 -1
  4. package/dist/cjs/doctor/contract.test.js +73 -2
  5. package/dist/cjs/doctor/contract.test.js.map +1 -1
  6. package/dist/cjs/generated/codama/instructions/closeRental.js +12 -0
  7. package/dist/cjs/generated/codama/instructions/closeRental.js.map +1 -1
  8. package/dist/cjs/idl/srsly.json +53 -2
  9. package/dist/cjs/instructions/closeContract.js +27 -41
  10. package/dist/cjs/instructions/closeContract.js.map +1 -1
  11. package/dist/cjs/instructions/closeContract.test.js +88 -0
  12. package/dist/cjs/instructions/closeContract.test.js.map +1 -1
  13. package/dist/cjs/instructions/closeContractThread.js +4 -4
  14. package/dist/cjs/instructions/closeContractThread.js.map +1 -1
  15. package/dist/cjs/instructions/closeRental.js +12 -1
  16. package/dist/cjs/instructions/closeRental.js.map +1 -1
  17. package/dist/cjs/instructions/deleteContractThread.js +4 -4
  18. package/dist/cjs/instructions/deleteContractThread.js.map +1 -1
  19. package/dist/cjs/legacy/closeContract.test.js +107 -0
  20. package/dist/cjs/legacy/closeContract.test.js.map +1 -0
  21. package/dist/cjs/package.json +1 -1
  22. package/dist/cjs/utils/fibers.js +12 -16
  23. package/dist/cjs/utils/fibers.js.map +1 -1
  24. package/dist/cjs/utils/fibers.test.js +21 -28
  25. package/dist/cjs/utils/fibers.test.js.map +1 -1
  26. package/dist/cjs/utils/rpc.js +43 -0
  27. package/dist/cjs/utils/rpc.js.map +1 -1
  28. package/dist/cjs/utils/rpc.test.js +60 -0
  29. package/dist/cjs/utils/rpc.test.js.map +1 -0
  30. package/dist/cjs/version.js +1 -1
  31. package/dist/esm/doctor/contract.js +107 -67
  32. package/dist/esm/doctor/contract.js.map +1 -1
  33. package/dist/esm/doctor/contract.test.js +73 -2
  34. package/dist/esm/doctor/contract.test.js.map +1 -1
  35. package/dist/esm/generated/codama/instructions/closeRental.js +12 -0
  36. package/dist/esm/generated/codama/instructions/closeRental.js.map +1 -1
  37. package/dist/esm/idl/srsly.json +53 -2
  38. package/dist/esm/instructions/closeContract.js +29 -43
  39. package/dist/esm/instructions/closeContract.js.map +1 -1
  40. package/dist/esm/instructions/closeContract.test.js +88 -0
  41. package/dist/esm/instructions/closeContract.test.js.map +1 -1
  42. package/dist/esm/instructions/closeContractThread.js +6 -6
  43. package/dist/esm/instructions/closeContractThread.js.map +1 -1
  44. package/dist/esm/instructions/closeRental.js +13 -2
  45. package/dist/esm/instructions/closeRental.js.map +1 -1
  46. package/dist/esm/instructions/deleteContractThread.js +6 -6
  47. package/dist/esm/instructions/deleteContractThread.js.map +1 -1
  48. package/dist/esm/legacy/closeContract.test.js +105 -0
  49. package/dist/esm/legacy/closeContract.test.js.map +1 -0
  50. package/dist/esm/package.json +1 -1
  51. package/dist/esm/utils/fibers.js +11 -15
  52. package/dist/esm/utils/fibers.js.map +1 -1
  53. package/dist/esm/utils/fibers.test.js +22 -29
  54. package/dist/esm/utils/fibers.test.js.map +1 -1
  55. package/dist/esm/utils/rpc.js +41 -0
  56. package/dist/esm/utils/rpc.js.map +1 -1
  57. package/dist/esm/utils/rpc.test.js +58 -0
  58. package/dist/esm/utils/rpc.test.js.map +1 -0
  59. package/dist/esm/version.js +1 -1
  60. package/dist/idl/srsly.json +53 -2
  61. package/dist/types/doctor/contract.d.ts +23 -1
  62. package/dist/types/doctor/contract.d.ts.map +1 -1
  63. package/dist/types/generated/codama/instructions/closeRental.d.ts +46 -1
  64. package/dist/types/generated/codama/instructions/closeRental.d.ts.map +1 -1
  65. package/dist/types/instructions/closeContract.d.ts +6 -3
  66. package/dist/types/instructions/closeContract.d.ts.map +1 -1
  67. package/dist/types/instructions/closeContractThread.d.ts.map +1 -1
  68. package/dist/types/instructions/closeRental.d.ts.map +1 -1
  69. package/dist/types/instructions/deleteContractThread.d.ts.map +1 -1
  70. package/dist/types/legacy/closeContract.test.d.ts +2 -0
  71. package/dist/types/legacy/closeContract.test.d.ts.map +1 -0
  72. package/dist/types/utils/fibers.d.ts +10 -9
  73. package/dist/types/utils/fibers.d.ts.map +1 -1
  74. package/dist/types/utils/rpc.d.ts +18 -0
  75. package/dist/types/utils/rpc.d.ts.map +1 -1
  76. package/dist/types/utils/rpc.test.d.ts +2 -0
  77. package/dist/types/utils/rpc.test.d.ts.map +1 -0
  78. package/dist/types/version.d.ts +1 -1
  79. package/package.json +1 -1
@@ -2,50 +2,43 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const vitest_1 = require("vitest");
4
4
  const kit_1 = require("@solana/kit");
5
- vitest_1.vi.mock('./rpc', () => ({ createRpc: vitest_1.vi.fn() }));
6
5
  vitest_1.vi.mock('../pda/thread', () => ({ deriveFiber: vitest_1.vi.fn() }));
7
- const rpc_1 = require("./rpc");
8
6
  const thread_1 = require("../pda/thread");
9
7
  const fibers_1 = require("./fibers");
10
8
  const THREAD = 'Sysvar1nstructions1111111111111111111111111';
11
9
  const FIBER_0 = 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v';
12
10
  const FIBER_1 = 'So11111111111111111111111111111111111111112';
13
- /** `getMultipleAccounts` returns `null` in the slot of an account that does not exist. */
14
- function mockAccounts(value) {
15
- const send = vitest_1.vi.fn().mockResolvedValue({ value });
16
- vitest_1.vi.mocked(rpc_1.createRpc).mockReturnValue({
17
- getMultipleAccounts: vitest_1.vi.fn().mockReturnValue({ send }),
18
- });
19
- return send;
20
- }
21
11
  (0, vitest_1.beforeEach)(() => {
22
12
  vitest_1.vi.clearAllMocks();
23
13
  vitest_1.vi.mocked(thread_1.deriveFiber).mockImplementation(async (_thread, index) => index === 0 ? FIBER_0 : FIBER_1);
24
14
  });
25
- (0, vitest_1.describe)('existingFiberAccounts', () => {
26
- (0, vitest_1.it)('drops a tracked fiber whose account is gone', async () => {
15
+ (0, vitest_1.describe)('trackedFiberAccounts', () => {
16
+ (0, vitest_1.it)('carries a tracked fiber whose account is gone', async () => {
27
17
  // The state the 2026-08-26 rent sweep left behind: the thread still tracks
28
- // both indices, but fiber 1's account was closed. Passing it makes the
29
- // fiber program deserialize an empty buffer and fail with
30
- // `InvalidFiberData`, which no caller can work around.
31
- mockAccounts([{ lamports: 9416880n }, null]);
32
- const accounts = await (0, fibers_1.existingFiberAccounts)(THREAD, [0, 1], 'https://rpc.example');
33
- (0, vitest_1.expect)(accounts).toEqual([{ address: FIBER_0, role: kit_1.AccountRole.WRITABLE }]);
18
+ // both indices, but fiber 1's account was closed. The id has to be
19
+ // accounted for or `thread_close` fails with `MissingFiberAccounts`, and
20
+ // passing the account is the only way to account for it — dropping the id
21
+ // is the thread program's call, and only it can edit `fiber_ids`.
22
+ const accounts = await (0, fibers_1.trackedFiberAccounts)(THREAD, [0, 1]);
23
+ (0, vitest_1.expect)(accounts).toEqual([
24
+ { address: FIBER_0, role: kit_1.AccountRole.WRITABLE },
25
+ { address: FIBER_1, role: kit_1.AccountRole.WRITABLE },
26
+ ]);
34
27
  });
35
- (0, vitest_1.it)('keeps every fiber when all of them exist', async () => {
36
- mockAccounts([{ lamports: 1n }, { lamports: 1n }]);
37
- const accounts = await (0, fibers_1.existingFiberAccounts)(THREAD, [0, 1], 'https://rpc.example');
28
+ (0, vitest_1.it)('carries every fiber when all of them exist', async () => {
29
+ const accounts = await (0, fibers_1.trackedFiberAccounts)(THREAD, [0, 1]);
38
30
  (0, vitest_1.expect)(accounts.map(a => a.address)).toEqual([FIBER_0, FIBER_1]);
39
31
  });
40
- (0, vitest_1.it)('returns nothing when the thread tracks nothing, without an RPC call', async () => {
41
- const send = mockAccounts([]);
42
- const accounts = await (0, fibers_1.existingFiberAccounts)(THREAD, [], 'https://rpc.example');
32
+ (0, vitest_1.it)('returns nothing when the thread tracks nothing', async () => {
33
+ const accounts = await (0, fibers_1.trackedFiberAccounts)(THREAD, []);
43
34
  (0, vitest_1.expect)(accounts).toEqual([]);
44
- (0, vitest_1.expect)(send).not.toHaveBeenCalled();
35
+ (0, vitest_1.expect)(thread_1.deriveFiber).not.toHaveBeenCalled();
45
36
  });
46
- (0, vitest_1.it)('drops every fiber when none survive', async () => {
47
- mockAccounts([null, null]);
48
- (0, vitest_1.expect)(await (0, fibers_1.existingFiberAccounts)(THREAD, [0, 1], 'https://rpc.example')).toEqual([]);
37
+ (0, vitest_1.it)('reads no account state, so it needs no RPC', async () => {
38
+ // Existence is not consulted at all — the addresses are pure computation
39
+ // from the thread and the ids.
40
+ await (0, fibers_1.trackedFiberAccounts)(THREAD, [0, 1]);
41
+ (0, vitest_1.expect)(thread_1.deriveFiber).toHaveBeenCalledTimes(2);
49
42
  });
50
43
  });
51
44
  //# sourceMappingURL=fibers.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"fibers.test.js","sourceRoot":"","sources":["../../../src/utils/fibers.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"fibers.test.js","sourceRoot":"","sources":["../../../src/utils/fibers.test.ts"],"names":[],"mappings":";;AAAA,mCAA8D;AAC9D,qCAAwD;AAExD,WAAE,CAAC,IAAI,CAAC,eAAe,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,WAAW,EAAE,WAAE,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;AAE3D,0CAA4C;AAC5C,qCAAgD;AAEhD,MAAM,MAAM,GAAG,6CAAwD,CAAC;AACxE,MAAM,OAAO,GAAG,8CAAyD,CAAC;AAC1E,MAAM,OAAO,GAAG,6CAAwD,CAAC;AAEzE,IAAA,mBAAU,EAAC,GAAG,EAAE;IACd,WAAE,CAAC,aAAa,EAAE,CAAC;IACnB,WAAE,CAAC,MAAM,CAAC,oBAAW,CAAC,CAAC,kBAAkB,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,CACjE,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAChC,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,IAAA,iBAAQ,EAAC,sBAAsB,EAAE,GAAG,EAAE;IACpC,IAAA,WAAE,EAAC,+CAA+C,EAAE,KAAK,IAAI,EAAE;QAC7D,2EAA2E;QAC3E,mEAAmE;QACnE,yEAAyE;QACzE,0EAA0E;QAC1E,kEAAkE;QAClE,MAAM,QAAQ,GAAG,MAAM,IAAA,6BAAoB,EAAC,MAAM,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QAE5D,IAAA,eAAM,EAAC,QAAQ,CAAC,CAAC,OAAO,CAAC;YACvB,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,iBAAW,CAAC,QAAQ,EAAE;YAChD,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,iBAAW,CAAC,QAAQ,EAAE;SACjD,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,IAAA,WAAE,EAAC,4CAA4C,EAAE,KAAK,IAAI,EAAE;QAC1D,MAAM,QAAQ,GAAG,MAAM,IAAA,6BAAoB,EAAC,MAAM,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QAE5D,IAAA,eAAM,EAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;IACnE,CAAC,CAAC,CAAC;IAEH,IAAA,WAAE,EAAC,gDAAgD,EAAE,KAAK,IAAI,EAAE;QAC9D,MAAM,QAAQ,GAAG,MAAM,IAAA,6BAAoB,EAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QAExD,IAAA,eAAM,EAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAC7B,IAAA,eAAM,EAAC,oBAAW,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;IAC7C,CAAC,CAAC,CAAC;IAEH,IAAA,WAAE,EAAC,4CAA4C,EAAE,KAAK,IAAI,EAAE;QAC1D,yEAAyE;QACzE,+BAA+B;QAC/B,MAAM,IAAA,6BAAoB,EAAC,MAAM,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QAE3C,IAAA,eAAM,EAAC,oBAAW,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC;IAC/C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -6,9 +6,11 @@
6
6
  * to add environment-specific configurations or debugging in the future.
7
7
  */
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.MAX_MULTIPLE_ACCOUNTS = void 0;
9
10
  exports.setRpcDebug = setRpcDebug;
10
11
  exports.isRpcDebugEnabled = isRpcDebugEnabled;
11
12
  exports.createRpc = createRpc;
13
+ exports.sendWithRetry = sendWithRetry;
12
14
  const kit_1 = require("@solana/kit");
13
15
  // Debug flag - can be set via setSdkConfig or environment
14
16
  let debugMode = false;
@@ -54,4 +56,45 @@ function createRpc(rpcUrl) {
54
56
  }
55
57
  return (0, kit_1.createSolanaRpc)(rpcUrl);
56
58
  }
59
+ /** Largest account list `getMultipleAccounts` accepts in one request. */
60
+ exports.MAX_MULTIPLE_ACCOUNTS = 100;
61
+ /** Providers signal "slow down" as 429, and the wording varies by host. */
62
+ function isRateLimited(error) {
63
+ const e = error;
64
+ if (e?.statusCode === 429 || e?.status === 429)
65
+ return true;
66
+ const message = String(e?.message ?? error);
67
+ return message.includes('429') || /too many requests/i.test(message);
68
+ }
69
+ const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
70
+ /**
71
+ * Run an RPC call, backing off and retrying when the provider rate-limits it.
72
+ *
73
+ * A 429 is not a result — it means the question was never asked, so a caller
74
+ * that treats it as one reports on state it never read. Backoff is exponential
75
+ * with jitter so workers that trip the limit together do not retry together.
76
+ *
77
+ * @param send - Performs the call. Invoked again on each retry.
78
+ * @param options.retries - Extra attempts after the first. Defaults to 5.
79
+ * @param options.baseDelayMs - Delay before the first retry. Defaults to 250.
80
+ * @throws The last error, once retries are exhausted or it is not a 429.
81
+ */
82
+ async function sendWithRetry(send, options = {}) {
83
+ const retries = options.retries ?? 5;
84
+ const baseDelayMs = options.baseDelayMs ?? 250;
85
+ let lastError;
86
+ for (let attempt = 0; attempt <= retries; attempt++) {
87
+ try {
88
+ return await send();
89
+ }
90
+ catch (error) {
91
+ lastError = error;
92
+ if (!isRateLimited(error) || attempt === retries)
93
+ throw error;
94
+ const backoff = baseDelayMs * 2 ** attempt;
95
+ await delay(backoff + Math.floor(Math.random() * backoff));
96
+ }
97
+ }
98
+ throw lastError;
99
+ }
57
100
  //# sourceMappingURL=rpc.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"rpc.js","sourceRoot":"","sources":["../../../src/utils/rpc.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;AAWH,kCAEC;AAKD,8CAEC;AAqBD,8BASC;AAhDD,qCAA2E;AAE3E,0DAA0D;AAC1D,IAAI,SAAS,GAAG,KAAK,CAAC;AAEtB;;;GAGG;AACH,SAAgB,WAAW,CAAC,OAAgB;IAC1C,SAAS,GAAG,OAAO,CAAC;AACtB,CAAC;AAED;;GAEG;AACH,SAAgB,iBAAiB;IAC/B,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,SAAgB,SAAS,CAAC,MAAc;IACtC,IAAI,SAAS,EAAE,CAAC;QACd,OAAO,CAAC,GAAG,CAAC,wCAAwC,EAAE;YACpD,GAAG,EAAE,MAAM;YACX,WAAW,EAAE,OAAO,MAAM,KAAK,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM;YAC/D,SAAS,EAAE,OAAO,SAAS,KAAK,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK;SAC1E,CAAC,CAAC;IACL,CAAC;IACD,OAAO,IAAA,qBAAe,EAAC,MAAM,CAAC,CAAC;AACjC,CAAC"}
1
+ {"version":3,"file":"rpc.js","sourceRoot":"","sources":["../../../src/utils/rpc.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;AAWH,kCAEC;AAKD,8CAEC;AAqBD,8BASC;AA2BD,sCAmBC;AA9FD,qCAA2E;AAE3E,0DAA0D;AAC1D,IAAI,SAAS,GAAG,KAAK,CAAC;AAEtB;;;GAGG;AACH,SAAgB,WAAW,CAAC,OAAgB;IAC1C,SAAS,GAAG,OAAO,CAAC;AACtB,CAAC;AAED;;GAEG;AACH,SAAgB,iBAAiB;IAC/B,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,SAAgB,SAAS,CAAC,MAAc;IACtC,IAAI,SAAS,EAAE,CAAC;QACd,OAAO,CAAC,GAAG,CAAC,wCAAwC,EAAE;YACpD,GAAG,EAAE,MAAM;YACX,WAAW,EAAE,OAAO,MAAM,KAAK,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM;YAC/D,SAAS,EAAE,OAAO,SAAS,KAAK,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK;SAC1E,CAAC,CAAC;IACL,CAAC;IACD,OAAO,IAAA,qBAAe,EAAC,MAAM,CAAC,CAAC;AACjC,CAAC;AAED,yEAAyE;AAC5D,QAAA,qBAAqB,GAAG,GAAG,CAAC;AAEzC,2EAA2E;AAC3E,SAAS,aAAa,CAAC,KAAc;IACnC,MAAM,CAAC,GAAG,KAAoE,CAAC;IAC/E,IAAI,CAAC,EAAE,UAAU,KAAK,GAAG,IAAI,CAAC,EAAE,MAAM,KAAK,GAAG;QAAE,OAAO,IAAI,CAAC;IAC5D,MAAM,OAAO,GAAG,MAAM,CAAC,CAAC,EAAE,OAAO,IAAI,KAAK,CAAC,CAAC;IAC5C,OAAO,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,oBAAoB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;AACvE,CAAC;AAED,MAAM,KAAK,GAAG,CAAC,EAAU,EAAE,EAAE,CAAC,IAAI,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;AAE9E;;;;;;;;;;;GAWG;AACI,KAAK,UAAU,aAAa,CACjC,IAAsB,EACtB,UAAsD,EAAE;IAExD,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,CAAC,CAAC;IACrC,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,GAAG,CAAC;IAE/C,IAAI,SAAkB,CAAC;IACvB,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,IAAI,OAAO,EAAE,OAAO,EAAE,EAAE,CAAC;QACpD,IAAI,CAAC;YACH,OAAO,MAAM,IAAI,EAAE,CAAC;QACtB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,SAAS,GAAG,KAAK,CAAC;YAClB,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,IAAI,OAAO,KAAK,OAAO;gBAAE,MAAM,KAAK,CAAC;YAC9D,MAAM,OAAO,GAAG,WAAW,GAAG,CAAC,IAAI,OAAO,CAAC;YAC3C,MAAM,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,OAAO,CAAC,CAAC,CAAC;QAC7D,CAAC;IACH,CAAC;IACD,MAAM,SAAS,CAAC;AAClB,CAAC"}
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ /** A 429 means the question never reached the provider, not that it answered. */
4
+ const vitest_1 = require("vitest");
5
+ const rpc_1 = require("./rpc");
6
+ (0, vitest_1.beforeEach)(() => {
7
+ vitest_1.vi.useFakeTimers();
8
+ });
9
+ (0, vitest_1.afterEach)(() => {
10
+ vitest_1.vi.useRealTimers();
11
+ });
12
+ /** Drives the fake clock until `promise` settles, so backoff sleeps resolve. */
13
+ async function runToCompletion(promise) {
14
+ const settled = promise.then(value => ({ ok: true, value }), error => ({ ok: false, error }));
15
+ await vitest_1.vi.runAllTimersAsync();
16
+ const outcome = await settled;
17
+ if (!outcome.ok)
18
+ throw outcome.error;
19
+ return outcome.value;
20
+ }
21
+ (0, vitest_1.describe)('sendWithRetry', () => {
22
+ (0, vitest_1.it)('returns the first result when nothing is rate limited', async () => {
23
+ const send = vitest_1.vi.fn().mockResolvedValue('ok');
24
+ await (0, vitest_1.expect)(runToCompletion((0, rpc_1.sendWithRetry)(send))).resolves.toBe('ok');
25
+ (0, vitest_1.expect)(send).toHaveBeenCalledTimes(1);
26
+ });
27
+ (0, vitest_1.it)('retries a 429 and returns the eventual success', async () => {
28
+ const send = vitest_1.vi
29
+ .fn()
30
+ .mockRejectedValueOnce(new Error('HTTP error (429): Too Many Requests'))
31
+ .mockRejectedValueOnce(new Error('HTTP error (429): Too Many Requests'))
32
+ .mockResolvedValue('ok');
33
+ await (0, vitest_1.expect)(runToCompletion((0, rpc_1.sendWithRetry)(send))).resolves.toBe('ok');
34
+ (0, vitest_1.expect)(send).toHaveBeenCalledTimes(3);
35
+ });
36
+ (0, vitest_1.it)('recognises a rate limit from a status code alone', async () => {
37
+ const err = Object.assign(new Error('rejected'), { statusCode: 429 });
38
+ const send = vitest_1.vi.fn().mockRejectedValueOnce(err).mockResolvedValue('ok');
39
+ await (0, vitest_1.expect)(runToCompletion((0, rpc_1.sendWithRetry)(send))).resolves.toBe('ok');
40
+ (0, vitest_1.expect)(send).toHaveBeenCalledTimes(2);
41
+ });
42
+ (0, vitest_1.it)('does not retry an error that is not a rate limit', async () => {
43
+ // Retrying a decode failure or a bad request just multiplies the damage.
44
+ const send = vitest_1.vi.fn().mockRejectedValue(new Error('Invalid param: bad address'));
45
+ await (0, vitest_1.expect)(runToCompletion((0, rpc_1.sendWithRetry)(send))).rejects.toThrow('Invalid param');
46
+ (0, vitest_1.expect)(send).toHaveBeenCalledTimes(1);
47
+ });
48
+ (0, vitest_1.it)('gives up after the retry budget and surfaces the rate limit', async () => {
49
+ // The caller has to be able to tell "still limited" from "answered", so the
50
+ // final failure propagates rather than resolving to something empty.
51
+ const send = vitest_1.vi.fn().mockRejectedValue(new Error('HTTP error (429): Too Many Requests'));
52
+ await (0, vitest_1.expect)(runToCompletion((0, rpc_1.sendWithRetry)(send, { retries: 2 }))).rejects.toThrow('429');
53
+ (0, vitest_1.expect)(send).toHaveBeenCalledTimes(3);
54
+ });
55
+ (0, vitest_1.it)('batches at the getMultipleAccounts ceiling', () => {
56
+ // Asking for more than this in one call is rejected outright by the RPC.
57
+ (0, vitest_1.expect)(rpc_1.MAX_MULTIPLE_ACCOUNTS).toBe(100);
58
+ });
59
+ });
60
+ //# sourceMappingURL=rpc.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rpc.test.js","sourceRoot":"","sources":["../../../src/utils/rpc.test.ts"],"names":[],"mappings":"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"}
@@ -3,5 +3,5 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.VERSION = void 0;
4
4
  // Auto-generated from package.json - DO NOT EDIT
5
5
  // Run "pnpm run generate:version" to update
6
- exports.VERSION = '5.5.2';
6
+ exports.VERSION = '6.1.0';
7
7
  //# sourceMappingURL=version.js.map
@@ -9,7 +9,7 @@ import { getAddressDecoder, getBase64Encoder } from '@solana/kit';
9
9
  import { getThreadDecoder } from '../generated/codama/types/thread';
10
10
  import { getConfigStateDecoder, getContractStateDecoder, getRentalStateDecoder, } from '../generated/codama/accounts';
11
11
  import { getRpcUrl } from '../utils/config';
12
- import { createRpc } from '../utils/rpc';
12
+ import { createRpc, sendWithRetry, MAX_MULTIPLE_ACCOUNTS } from '../utils/rpc';
13
13
  import { deriveFiber } from '../pda/thread';
14
14
  import { deriveConfig } from '../pda/srsly';
15
15
  import { deriveAssociatedTokenAccount } from '../pda/token';
@@ -81,15 +81,21 @@ export async function diagnoseContract(contractAddress, options = {}) {
81
81
  const rpc = createRpc(options.rpcUrl || getRpcUrl());
82
82
  const now = options.nowSeconds ?? BigInt(Math.floor(Date.now() / 1000));
83
83
  const contractAddr = contractAddress;
84
- // Config rides along with the contract rather than costing a third round
85
- // trip: `atlas_mint` is what derives the owner's payout ATA, and the
86
- // derivation has to happen before the second batch can probe it.
87
- const configAddr = await deriveConfig();
88
- const first = await rpc
89
- .getMultipleAccounts([contractAddr, configAddr], { encoding: 'base64' })
90
- .send();
91
- const contract = decodeOrNull(first.value[0]?.data, bytes => getContractStateDecoder().decode(bytes));
92
- const config = decodeOrNull(first.value[1]?.data, bytes => getConfigStateDecoder().decode(bytes));
84
+ // Config rides along with the contract: `atlas_mint` derives the owner's
85
+ // payout ATA, which the second batch needs. A bulk scan has already read both
86
+ // in batches, so it passes them in rather than paying for them per contract.
87
+ let contract;
88
+ let config;
89
+ if (options.prefetched) {
90
+ contract = options.prefetched.contract;
91
+ config = options.prefetched.config;
92
+ }
93
+ else {
94
+ const configAddr = await deriveConfig();
95
+ const first = await sendWithRetry(() => rpc.getMultipleAccounts([contractAddr, configAddr], { encoding: 'base64' }).send());
96
+ contract = decodeOrNull(first.value[0]?.data, bytes => getContractStateDecoder().decode(bytes));
97
+ config = decodeOrNull(first.value[1]?.data, bytes => getConfigStateDecoder().decode(bytes));
98
+ }
93
99
  const findings = [];
94
100
  const empty = {
95
101
  contract: contractAddr,
@@ -113,81 +119,96 @@ export async function diagnoseContract(contractAddress, options = {}) {
113
119
  ownerClaimable: contract.ownerClaimable,
114
120
  feeClaimable: contract.feeClaimable,
115
121
  };
116
- if (contract.thread === SYSTEM_PROGRAM_ID) {
117
- findings.push({
118
- code: 'threadNotConfigured',
119
- severity: 'warning',
120
- message: 'Contract has no automation thread. Nothing settles on its own.',
121
- });
122
- return { ...empty, owner: contract.owner, fleet: contract.fleet, healthy: true, atRisk };
123
- }
124
- const thread = contract.thread;
122
+ const thread = contract.thread === SYSTEM_PROGRAM_ID ? null : contract.thread;
125
123
  const hasActiveRental = contract.activeRental !== SYSTEM_PROGRAM_ID;
126
- // One round trip for the thread, both fiber PDAs, the active rental, and the
127
- // owner's payout ATA.
128
- const fiberAddresses = await Promise.all(KNOWN_FIBER_INDICES.map(i => deriveFiber(thread, i)));
124
+ // One round trip for the thread, both fiber PDAs, the active rental and the
125
+ // owner's payout ATA. Built positionally because a threadless contract still
126
+ // has a rental worth reading — bailing on the thread is what used to report
127
+ // an overdue rental as healthy.
128
+ const fiberAddresses = thread
129
+ ? await Promise.all(KNOWN_FIBER_INDICES.map(i => deriveFiber(thread, i)))
130
+ : [];
129
131
  const ownerTokenAccount = config
130
132
  ? await deriveAssociatedTokenAccount(contract.owner, config.atlasMint)
131
133
  : null;
132
134
  const probe = [
133
- thread,
134
- ...fiberAddresses,
135
+ ...(thread ? [thread, ...fiberAddresses] : []),
135
136
  ...(hasActiveRental ? [contract.activeRental] : []),
136
137
  ...(ownerTokenAccount ? [ownerTokenAccount] : []),
137
138
  ];
138
- const multi = await rpc.getMultipleAccounts(probe, { encoding: 'base64' }).send();
139
- const [threadAcct, ...rest] = multi.value;
140
- const fiberAccts = rest.slice(0, KNOWN_FIBER_INDICES.length);
141
- const activeRentalAcct = hasActiveRental ? rest[KNOWN_FIBER_INDICES.length] : null;
142
- const ownerTokenAcct = ownerTokenAccount
143
- ? rest[KNOWN_FIBER_INDICES.length + (hasActiveRental ? 1 : 0)]
144
- : null;
145
- if (!threadAcct) {
139
+ const multi = await sendWithRetry(() => rpc.getMultipleAccounts(probe, { encoding: 'base64' }).send());
140
+ let slot = 0;
141
+ const threadAcct = thread ? multi.value[slot++] : null;
142
+ const fiberAccts = thread ? KNOWN_FIBER_INDICES.map(() => multi.value[slot++]) : [];
143
+ const activeRentalAcct = hasActiveRental ? multi.value[slot++] : null;
144
+ const ownerTokenAcct = ownerTokenAccount ? multi.value[slot++] : null;
145
+ const threadLamports = threadAcct ? BigInt(threadAcct.lamports) : null;
146
+ const missingFibers = [];
147
+ if (!thread) {
148
+ findings.push({
149
+ code: 'threadNotConfigured',
150
+ severity: 'warning',
151
+ message: 'Contract has no automation thread. Nothing settles on its own.',
152
+ });
153
+ }
154
+ else if (!threadAcct) {
146
155
  findings.push({
147
156
  code: 'threadMissing',
148
157
  severity: 'critical',
149
158
  message: `Thread ${thread} does not exist. Recreate it with createContractThread — ` +
150
159
  'rewriting a fiber would strand rent in an account nothing references.',
151
160
  });
152
- return { ...empty, owner: contract.owner, fleet: contract.fleet, thread, atRisk };
153
161
  }
154
- const threadLamports = BigInt(threadAcct.lamports);
155
- const threadData = decodeOrNull(threadAcct.data, bytes => getThreadDecoder().decode(bytes.subarray(DISCRIMINATOR_SIZE)));
156
- const missingFibers = [];
157
- if (threadData) {
158
- const tracked = new Set(Array.from(threadData.fiberIds));
159
- KNOWN_FIBER_INDICES.forEach((index, slot) => {
160
- // Only a *tracked* index is a fault. Fiber 1 is legitimately absent until
161
- // a rental is accepted, so its absence alone proves nothing.
162
- if (!tracked.has(index))
163
- return;
164
- if (fiberAccts[slot])
165
- return;
166
- missingFibers.push(index);
167
- findings.push({
168
- code: 'fiberMissing',
169
- severity: 'critical',
170
- message: `Thread tracks fiber ${index} but ${fiberAddresses[slot]} does not exist. ` +
171
- 'Every execution fails to build, so the contract cannot settle. ' +
172
- 'Plan the repair with `antegen thread doctor --plan`, then replay it.',
173
- fiberIndex: index,
174
- fiber: fiberAddresses[slot],
162
+ else {
163
+ const threadData = decodeOrNull(threadAcct.data, bytes => getThreadDecoder().decode(bytes.subarray(DISCRIMINATOR_SIZE)));
164
+ if (threadData) {
165
+ const tracked = new Set(Array.from(threadData.fiberIds));
166
+ KNOWN_FIBER_INDICES.forEach((index, i) => {
167
+ // Only a *tracked* index is a fault. Fiber 1 is legitimately absent
168
+ // until a rental is accepted, so its absence alone proves nothing.
169
+ if (!tracked.has(index))
170
+ return;
171
+ if (fiberAccts[i])
172
+ return;
173
+ missingFibers.push(index);
174
+ findings.push({
175
+ code: 'fiberMissing',
176
+ severity: 'critical',
177
+ message: `Thread tracks fiber ${index} but ${fiberAddresses[i]} does not exist. ` +
178
+ 'Every execution fails to build, so the contract cannot settle. ' +
179
+ 'Plan the repair with `antegen thread doctor --plan`, then replay it.',
180
+ fiberIndex: index,
181
+ fiber: fiberAddresses[i],
182
+ });
175
183
  });
176
- });
177
- if (threadData.paused && hasActiveRental) {
184
+ if (threadData.paused && hasActiveRental) {
185
+ findings.push({
186
+ code: 'threadPaused',
187
+ severity: 'critical',
188
+ message: 'A rental is active but the thread is paused, so nothing will settle.',
189
+ });
190
+ }
191
+ }
192
+ if (threadLamports !== null && threadLamports < THREAD_LAMPORTS_FLOOR) {
178
193
  findings.push({
179
- code: 'threadPaused',
180
- severity: 'critical',
181
- message: 'A rental is active but the thread is paused, so nothing will settle.',
194
+ code: 'threadUnderfunded',
195
+ severity: 'warning',
196
+ message: `Thread holds ${threadLamports} lamports, below the ${THREAD_LAMPORTS_FLOOR} floor ` +
197
+ 'needed to keep paying for its own executions. Top it up with updateContractThread.',
182
198
  });
183
199
  }
184
200
  }
185
- if (threadLamports < THREAD_LAMPORTS_FLOOR) {
201
+ // `contract_process` is the only automated route from `to_close` to a closed
202
+ // contract. Latched without a live thread, nothing will ever act on it.
203
+ if (contract.toClose && !threadAcct) {
186
204
  findings.push({
187
- code: 'threadUnderfunded',
188
- severity: 'warning',
189
- message: `Thread holds ${threadLamports} lamports, below the ${THREAD_LAMPORTS_FLOOR} floor ` +
190
- 'needed to keep paying for its own executions. Top it up with updateContractThread.',
205
+ code: 'closeStranded',
206
+ severity: 'critical',
207
+ message: 'Contract is flagged to_close but has no live thread to complete the close. ' +
208
+ (hasActiveRental
209
+ ? 'Nothing will close the rental when it ends, and nothing will close the contract after. '
210
+ : 'The slot is already empty, so the close only needs running. ') +
211
+ 'Run closeContract to finish it.',
191
212
  });
192
213
  }
193
214
  if (ownerTokenAccount && !ownerTokenAcct) {
@@ -252,14 +273,33 @@ export async function diagnoseContract(contractAddress, options = {}) {
252
273
  */
253
274
  export async function diagnoseAllContracts(options = {}) {
254
275
  const addresses = await fetchAllContractAddresses(options.rpcUrl);
255
- const concurrency = Math.max(1, options.concurrency ?? 8);
276
+ const concurrency = Math.max(1, options.concurrency ?? 4);
256
277
  const results = new Array(addresses.length);
257
278
  let cursor = 0;
258
279
  let done = 0;
280
+ // Read every contract up front, `MAX_MULTIPLE_ACCOUNTS` at a time, plus the
281
+ // one config account they all share. Diagnosing them individually asked for
282
+ // the same two accounts per contract, so a 407-contract sweep opened with 407
283
+ // round trips before probing anything — enough to exhaust a provider's rate
284
+ // limit and have most of the scan come back 429 rather than diagnosed.
285
+ const rpc = createRpc(options.rpcUrl || getRpcUrl());
286
+ const configAddr = await deriveConfig();
287
+ const config = decodeOrNull((await sendWithRetry(() => rpc.getMultipleAccounts([configAddr], { encoding: 'base64' }).send())).value[0]?.data, bytes => getConfigStateDecoder().decode(bytes));
288
+ const contracts = new Map();
289
+ for (let i = 0; i < addresses.length; i += MAX_MULTIPLE_ACCOUNTS) {
290
+ const chunk = addresses.slice(i, i + MAX_MULTIPLE_ACCOUNTS);
291
+ const batch = await sendWithRetry(() => rpc.getMultipleAccounts(chunk, { encoding: 'base64' }).send());
292
+ chunk.forEach((addr, j) => {
293
+ contracts.set(addr, decodeOrNull(batch.value[j]?.data, bytes => getContractStateDecoder().decode(bytes)));
294
+ });
295
+ }
259
296
  async function worker() {
260
297
  for (let i = cursor++; i < addresses.length; i = cursor++) {
261
298
  try {
262
- results[i] = await diagnoseContract(addresses[i], options);
299
+ results[i] = await diagnoseContract(addresses[i], {
300
+ ...options,
301
+ prefetched: { contract: contracts.get(addresses[i]) ?? null, config },
302
+ });
263
303
  }
264
304
  catch (e) {
265
305
  // One unreadable contract must not kill a full scan. Report it as a
@@ -272,7 +312,7 @@ export async function diagnoseAllContracts(options = {}) {
272
312
  healthy: false,
273
313
  findings: [
274
314
  {
275
- code: 'threadNotConfigured',
315
+ code: 'diagnosisFailed',
276
316
  severity: 'warning',
277
317
  message: `Could not diagnose: ${e.message}`,
278
318
  },
@@ -1 +1 @@
1
- {"version":3,"file":"contract.js","sourceRoot":"","sources":["../../../src/doctor/contract.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAgB,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAChF,OAAO,EAAE,gBAAgB,EAAe,MAAM,kCAAkC,CAAC;AACjF,OAAO,EACL,qBAAqB,EACrB,uBAAuB,EACvB,qBAAqB,GACtB,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,4BAA4B,EAAE,MAAM,cAAc,CAAC;AAC5D,OAAO,EAAE,yBAAyB,EAAE,MAAM,sBAAsB,CAAC;AAEjE,2EAA2E;AAC3E,MAAM,iBAAiB,GAAG,kCAAkC,CAAC;AAE7D,0CAA0C;AAC1C,MAAM,kBAAkB,GAAG,CAAC,CAAC;AAE7B;;;;GAIG;AACH,MAAM,mBAAmB,GAAG,CAAC,CAAC,EAAE,CAAC,CAAU,CAAC;AAE5C;;;;GAIG;AACH,MAAM,qBAAqB,GAAG,QAAU,CAAC;AAEzC,2EAA2E;AAC3E,MAAM,wBAAwB,GAAG,MAAO,CAAC;AAiFzC,MAAM,MAAM,GAAG,gBAAgB,EAAE,CAAC;AAElC;;;;;;;;;;GAUG;AACH,MAAM,UAAU,kBAAkB,CAAC,MAA0B;IAC3D,OAAO,iBAAiB,EAAE,CAAC,MAAM,CAAC,IAAI,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;AAC/D,CAAC;AAED,SAAS,YAAY,CACnB,IAAoD,EACpD,MAAgC;IAEhC,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IACnD,IAAI,CAAC;QACH,OAAO,MAAM,CAAC,IAAI,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACxD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,eAAiC,EACjC,UAA2B,EAAE;IAE7B,MAAM,GAAG,GAAG,SAAS,CAAC,OAAO,CAAC,MAAM,IAAI,SAAS,EAAE,CAAC,CAAC;IACrD,MAAM,GAAG,GAAG,OAAO,CAAC,UAAU,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC;IACxE,MAAM,YAAY,GAAG,eAA0B,CAAC;IAEhD,yEAAyE;IACzE,qEAAqE;IACrE,iEAAiE;IACjE,MAAM,UAAU,GAAG,MAAM,YAAY,EAAE,CAAC;IACxC,MAAM,KAAK,GAAG,MAAM,GAAG;SACpB,mBAAmB,CAAC,CAAC,YAAY,EAAE,UAAU,CAAC,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC;SACvE,IAAI,EAAE,CAAC;IACV,MAAM,QAAQ,GAAG,YAAY,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,EAAE,CAC1D,uBAAuB,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CACxC,CAAC;IACF,MAAM,MAAM,GAAG,YAAY,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,EAAE,CAAC,qBAAqB,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IAElG,MAAM,QAAQ,GAAsB,EAAE,CAAC;IACvC,MAAM,KAAK,GAAsB;QAC/B,QAAQ,EAAE,YAAY;QACtB,KAAK,EAAE,IAAI;QACX,KAAK,EAAE,IAAI;QACX,MAAM,EAAE,IAAI;QACZ,OAAO,EAAE,KAAK;QACd,QAAQ;QACR,aAAa,EAAE,EAAE;QACjB,MAAM,EAAE,EAAE,kBAAkB,EAAE,EAAE,EAAE,cAAc,EAAE,EAAE,EAAE,YAAY,EAAE,EAAE,EAAE;QACxE,cAAc,EAAE,IAAI;QACpB,iBAAiB,EAAE,IAAI;KACxB,CAAC;IAEF,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,2EAA2E;QAC3E,mCAAmC;QACnC,MAAM,IAAI,KAAK,CAAC,oCAAoC,eAAe,EAAE,CAAC,CAAC;IACzE,CAAC;IAED,MAAM,MAAM,GAAmB;QAC7B,kBAAkB,EAAE,EAAE;QACtB,cAAc,EAAE,QAAQ,CAAC,cAAc;QACvC,YAAY,EAAE,QAAQ,CAAC,YAAY;KACpC,CAAC;IAEF,IAAI,QAAQ,CAAC,MAAM,KAAK,iBAAiB,EAAE,CAAC;QAC1C,QAAQ,CAAC,IAAI,CAAC;YACZ,IAAI,EAAE,qBAAqB;YAC3B,QAAQ,EAAE,SAAS;YACnB,OAAO,EAAE,gEAAgE;SAC1E,CAAC,CAAC;QACH,OAAO,EAAE,GAAG,KAAK,EAAE,KAAK,EAAE,QAAQ,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IAC3F,CAAC;IAED,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC;IAC/B,MAAM,eAAe,GAAG,QAAQ,CAAC,YAAY,KAAK,iBAAiB,CAAC;IAEpE,6EAA6E;IAC7E,sBAAsB;IACtB,MAAM,cAAc,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,WAAW,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/F,MAAM,iBAAiB,GAAG,MAAM;QAC9B,CAAC,CAAC,MAAM,4BAA4B,CAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,SAAS,CAAC;QACtE,CAAC,CAAC,IAAI,CAAC;IACT,MAAM,KAAK,GAAc;QACvB,MAAM;QACN,GAAG,cAAc;QACjB,GAAG,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACnD,GAAG,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;KAClD,CAAC;IACF,MAAM,KAAK,GAAG,MAAM,GAAG,CAAC,mBAAmB,CAAC,KAAK,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAClF,MAAM,CAAC,UAAU,EAAE,GAAG,IAAI,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC;IAC1C,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,mBAAmB,CAAC,MAAM,CAAC,CAAC;IAC7D,MAAM,gBAAgB,GAAG,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IACnF,MAAM,cAAc,GAAG,iBAAiB;QACtC,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC,MAAM,GAAG,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC9D,CAAC,CAAC,IAAI,CAAC;IAET,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,QAAQ,CAAC,IAAI,CAAC;YACZ,IAAI,EAAE,eAAe;YACrB,QAAQ,EAAE,UAAU;YACpB,OAAO,EACL,UAAU,MAAM,2DAA2D;gBAC3E,uEAAuE;SAC1E,CAAC,CAAC;QACH,OAAO,EAAE,GAAG,KAAK,EAAE,KAAK,EAAE,QAAQ,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;IACpF,CAAC;IAED,MAAM,cAAc,GAAG,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;IACnD,MAAM,UAAU,GAAG,YAAY,CAAS,UAAU,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,CAC/D,gBAAgB,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,kBAAkB,CAAC,CAAC,CAC9D,CAAC;IAEF,MAAM,aAAa,GAAa,EAAE,CAAC;IACnC,IAAI,UAAU,EAAE,CAAC;QACf,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC;QACzD,mBAAmB,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;YAC1C,0EAA0E;YAC1E,6DAA6D;YAC7D,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC;gBAAE,OAAO;YAChC,IAAI,UAAU,CAAC,IAAI,CAAC;gBAAE,OAAO;YAC7B,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAC1B,QAAQ,CAAC,IAAI,CAAC;gBACZ,IAAI,EAAE,cAAc;gBACpB,QAAQ,EAAE,UAAU;gBACpB,OAAO,EACL,uBAAuB,KAAK,QAAQ,cAAc,CAAC,IAAI,CAAC,mBAAmB;oBAC3E,iEAAiE;oBACjE,sEAAsE;gBACxE,UAAU,EAAE,KAAK;gBACjB,KAAK,EAAE,cAAc,CAAC,IAAI,CAAC;aAC5B,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,IAAI,UAAU,CAAC,MAAM,IAAI,eAAe,EAAE,CAAC;YACzC,QAAQ,CAAC,IAAI,CAAC;gBACZ,IAAI,EAAE,cAAc;gBACpB,QAAQ,EAAE,UAAU;gBACpB,OAAO,EAAE,sEAAsE;aAChF,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,IAAI,cAAc,GAAG,qBAAqB,EAAE,CAAC;QAC3C,QAAQ,CAAC,IAAI,CAAC;YACZ,IAAI,EAAE,mBAAmB;YACzB,QAAQ,EAAE,SAAS;YACnB,OAAO,EACL,gBAAgB,cAAc,wBAAwB,qBAAqB,SAAS;gBACpF,oFAAoF;SACvF,CAAC,CAAC;IACL,CAAC;IAED,IAAI,iBAAiB,IAAI,CAAC,cAAc,EAAE,CAAC;QACzC,yEAAyE;QACzE,0EAA0E;QAC1E,wEAAwE;QACxE,qCAAqC;QACrC,QAAQ,CAAC,IAAI,CAAC;YACZ,IAAI,EAAE,0BAA0B;YAChC,QAAQ,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS;YACnD,OAAO,EACL,yBAAyB,iBAAiB,+CAA+C;gBACzF,GAAG,QAAQ,CAAC,cAAc,wCAAwC;gBAClE,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,0CAA0C,CAAC,CAAC,CAAC,EAAE,CAAC;gBACpE,mEAAmE;gBACnE,wDAAwD;SAC3D,CAAC,CAAC;IACL,CAAC;IAED,MAAM,YAAY,GAAG,gBAAgB;QACnC,CAAC,CAAC,YAAY,CAAC,gBAAgB,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,CAAC,qBAAqB,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACrF,CAAC,CAAC,IAAI,CAAC;IAET,IAAI,YAAY,EAAE,CAAC;QACjB,MAAM,CAAC,kBAAkB,GAAG,YAAY,CAAC,MAAM,CAAC;QAEhD,IAAI,YAAY,CAAC,OAAO,GAAG,GAAG,EAAE,CAAC;YAC/B,QAAQ,CAAC,IAAI,CAAC;gBACZ,IAAI,EAAE,eAAe;gBACrB,QAAQ,EAAE,UAAU;gBACpB,OAAO,EACL,uBAAuB,GAAG,GAAG,YAAY,CAAC,OAAO,iCAAiC;oBAClF,GAAG,YAAY,CAAC,MAAM,oCAAoC;aAC7D,CAAC,CAAC;QACL,CAAC;QACD,MAAM,MAAM,GAAG,GAAG,GAAG,YAAY,CAAC,WAAW,CAAC;QAC9C,IAAI,MAAM,GAAG,wBAAwB,IAAI,YAAY,CAAC,WAAW,GAAG,EAAE,EAAE,CAAC;YACvE,QAAQ,CAAC,IAAI,CAAC;gBACZ,IAAI,EAAE,mBAAmB;gBACzB,QAAQ,EAAE,UAAU;gBACpB,OAAO,EAAE,oCAAoC,MAAM,IAAI;aACxD,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,OAAO;QACL,QAAQ,EAAE,YAAY;QACtB,KAAK,EAAE,QAAQ,CAAC,KAAK;QACrB,KAAK,EAAE,QAAQ,CAAC,KAAK;QACrB,MAAM;QACN,OAAO,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,UAAU,CAAC;QACvD,QAAQ;QACR,aAAa;QACb,MAAM;QACN,cAAc;QACd,iBAAiB;KAClB,CAAC;AACJ,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,UAKI,EAAE;IAEN,MAAM,SAAS,GAAG,MAAM,yBAAyB,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAClE,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,WAAW,IAAI,CAAC,CAAC,CAAC;IAC1D,MAAM,OAAO,GAAwB,IAAI,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IACjE,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,IAAI,IAAI,GAAG,CAAC,CAAC;IAEb,KAAK,UAAU,MAAM;QACnB,KAAK,IAAI,CAAC,GAAG,MAAM,EAAE,EAAE,CAAC,GAAG,SAAS,CAAC,MAAM,EAAE,CAAC,GAAG,MAAM,EAAE,EAAE,CAAC;YAC1D,IAAI,CAAC;gBACH,OAAO,CAAC,CAAC,CAAC,GAAG,MAAM,gBAAgB,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;YAC7D,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACX,oEAAoE;gBACpE,wDAAwD;gBACxD,OAAO,CAAC,CAAC,CAAC,GAAG;oBACX,QAAQ,EAAE,SAAS,CAAC,CAAC,CAAC;oBACtB,KAAK,EAAE,IAAI;oBACX,KAAK,EAAE,IAAI;oBACX,MAAM,EAAE,IAAI;oBACZ,OAAO,EAAE,KAAK;oBACd,QAAQ,EAAE;wBACR;4BACE,IAAI,EAAE,qBAAqB;4BAC3B,QAAQ,EAAE,SAAS;4BACnB,OAAO,EAAE,uBAAwB,CAAW,CAAC,OAAO,EAAE;yBACvD;qBACF;oBACD,aAAa,EAAE,EAAE;oBACjB,MAAM,EAAE,EAAE,kBAAkB,EAAE,EAAE,EAAE,cAAc,EAAE,EAAE,EAAE,YAAY,EAAE,EAAE,EAAE;oBACxE,cAAc,EAAE,IAAI;oBACpB,iBAAiB,EAAE,IAAI;iBACxB,CAAC;YACJ,CAAC;YACD,OAAO,CAAC,UAAU,EAAE,CAAC,EAAE,IAAI,EAAE,SAAS,CAAC,MAAM,CAAC,CAAC;QACjD,CAAC;IACH,CAAC;IAED,MAAM,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,SAAS,CAAC,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC;IAC3F,OAAO,OAAO,CAAC;AACjB,CAAC"}
1
+ {"version":3,"file":"contract.js","sourceRoot":"","sources":["../../../src/doctor/contract.ts"],"names":[],"mappings":"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"}
@@ -4,6 +4,10 @@ vi.mock('../utils/config', () => ({
4
4
  }));
5
5
  vi.mock('../utils/rpc', () => ({
6
6
  createRpc: vi.fn(),
7
+ // Pass straight through: retry behaviour is covered in the rpc suite, and
8
+ // stubbing it out here would hide a call the diagnosis actually makes.
9
+ sendWithRetry: vi.fn((send) => send()),
10
+ MAX_MULTIPLE_ACCOUNTS: 100,
7
11
  }));
8
12
  vi.mock('../pda/thread', () => ({
9
13
  deriveFiber: vi.fn(),
@@ -58,7 +62,7 @@ function setup(f = {}) {
58
62
  decode: () => ({
59
63
  owner: OWNER,
60
64
  fleet: FLEET,
61
- thread: THREAD,
65
+ thread: f.thread ?? THREAD,
62
66
  activeRental: hasActiveRental ? ACTIVE_RENTAL : SYSTEM,
63
67
  ownerClaimable: f.ownerClaimable ?? 500n,
64
68
  feeClaimable: 10n,
@@ -72,7 +76,11 @@ function setup(f = {}) {
72
76
  decode: () => ({ fiberIds: [0], paused: false, id: [] }),
73
77
  });
74
78
  vi.mocked(getRentalStateDecoder).mockReturnValue({
75
- decode: () => ({ escrow: 1000n, endTime: NOW + 86400n, lastPayment: NOW - 60n }),
79
+ decode: () => ({
80
+ escrow: 1000n,
81
+ endTime: f.rentalEndTime ?? NOW + 86400n,
82
+ lastPayment: NOW - 60n,
83
+ }),
76
84
  });
77
85
  // The second batch answers by address rather than by position, so the
78
86
  // diagnosis has to index its own probe correctly to read the right account.
@@ -138,4 +146,67 @@ describe('diagnoseContract — owner token account', () => {
138
146
  expect(rpc.getMultipleAccounts).toHaveBeenCalledTimes(2);
139
147
  });
140
148
  });
149
+ describe('diagnoseContract — a contract with no thread is still diagnosed', () => {
150
+ // Bailing on `threadNotConfigured` used to end the diagnosis before the
151
+ // rental was read, and return `healthy: true` regardless.
152
+ it('reports the overdue rental it used to skip', async () => {
153
+ setup({ thread: SYSTEM, rentalEndTime: NOW - 3600n, present: [ACTIVE_RENTAL, OWNER_ATA] });
154
+ const dx = await diagnoseContract(CONTRACT, { nowSeconds: NOW });
155
+ const overdue = dx.findings.find(f => f.code === 'rentalOverdue');
156
+ expect(overdue).toBeDefined();
157
+ expect(overdue.severity).toBe('critical');
158
+ expect(dx.healthy).toBe(false);
159
+ });
160
+ it('carries the escrow still held, rather than reporting zero at risk', async () => {
161
+ setup({ thread: SYSTEM, rentalEndTime: NOW - 3600n, present: [ACTIVE_RENTAL, OWNER_ATA] });
162
+ const dx = await diagnoseContract(CONTRACT, { nowSeconds: NOW });
163
+ expect(dx.atRisk.activeRentalEscrow).toBe(1000n);
164
+ expect(dx.atRisk.ownerClaimable).toBe(500n);
165
+ });
166
+ it('flags a to_close contract with no thread as stranded', async () => {
167
+ setup({ thread: SYSTEM, toClose: true, present: [ACTIVE_RENTAL, OWNER_ATA] });
168
+ const dx = await diagnoseContract(CONTRACT, { nowSeconds: NOW });
169
+ const stranded = dx.findings.find(f => f.code === 'closeStranded');
170
+ expect(stranded).toBeDefined();
171
+ expect(stranded.severity).toBe('critical');
172
+ expect(stranded.message).toContain('Nothing will close the rental when it ends');
173
+ expect(dx.healthy).toBe(false);
174
+ });
175
+ it('says the close only needs running when the slot is already empty', async () => {
176
+ setup({ thread: SYSTEM, toClose: true, hasActiveRental: false, present: [OWNER_ATA] });
177
+ const dx = await diagnoseContract(CONTRACT, { nowSeconds: NOW });
178
+ const stranded = dx.findings.find(f => f.code === 'closeStranded');
179
+ expect(stranded.message).toContain('only needs running');
180
+ });
181
+ it('stays healthy when it is merely idle', async () => {
182
+ // No thread, no rental, not closing. Nothing is meant to happen, so a
183
+ // warning is the whole story — this must not become noise on every
184
+ // unautomated contract.
185
+ setup({ thread: SYSTEM, hasActiveRental: false, present: [OWNER_ATA] });
186
+ const dx = await diagnoseContract(CONTRACT, { nowSeconds: NOW });
187
+ expect(dx.findings.map(f => f.code)).toEqual(['threadNotConfigured']);
188
+ expect(dx.healthy).toBe(true);
189
+ });
190
+ it('probes only the accounts that exist for it', async () => {
191
+ // No thread means no thread account and no fibers in the batch. Indexing
192
+ // as though they were there reads the rental out of the wrong slot.
193
+ const rpc = setup({ thread: SYSTEM, present: [ACTIVE_RENTAL, OWNER_ATA] });
194
+ const dx = await diagnoseContract(CONTRACT, { nowSeconds: NOW });
195
+ expect(rpc.getMultipleAccounts.mock.calls[1][0]).toEqual([ACTIVE_RENTAL, OWNER_ATA]);
196
+ expect(dx.thread).toBeNull();
197
+ expect(dx.threadLamports).toBeNull();
198
+ expect(dx.findings.map(f => f.code)).not.toContain('ownerTokenAccountMissing');
199
+ });
200
+ });
201
+ describe('diagnoseContract — a thread that vanished is still diagnosed', () => {
202
+ it('reports both the missing thread and the overdue rental', async () => {
203
+ // `threadMissing` also used to return early, hiding everything after it.
204
+ setup({ rentalEndTime: NOW - 3600n, present: [ACTIVE_RENTAL, OWNER_ATA] });
205
+ const dx = await diagnoseContract(CONTRACT, { nowSeconds: NOW });
206
+ const codes = dx.findings.map(f => f.code);
207
+ expect(codes).toContain('threadMissing');
208
+ expect(codes).toContain('rentalOverdue');
209
+ expect(dx.atRisk.activeRentalEscrow).toBe(1000n);
210
+ });
211
+ });
141
212
  //# sourceMappingURL=contract.test.js.map