@ar.io/sdk 4.0.0-solana.37 → 4.0.0-solana.38

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.
@@ -2661,6 +2661,7 @@ export class SolanaARIOWriteable extends SolanaARIOReadable {
2661
2661
  const enableCompound = opts.enableCompound ?? true;
2662
2662
  const compoundMinPending = opts.compoundMinPendingRewards ?? 0;
2663
2663
  const enableDemandFactorRoll = opts.enableDemandFactorRoll ?? true;
2664
+ const enablePrune = opts.enablePrune ?? true;
2664
2665
  const now = opts.now ?? Math.floor(Date.now() / 1000);
2665
2666
  const settings = await this.getEpochSettingsFull();
2666
2667
  if (!settings.enabled)
@@ -2714,9 +2715,19 @@ export class SolanaARIOWriteable extends SolanaARIOReadable {
2714
2715
  const { id } = await this.prescribeWithPrediction(targetEpochIndex, nameRegistryAccount);
2715
2716
  return { action: 'prescribe', epochIndex: targetEpochIndex, txId: id };
2716
2717
  }
2717
- // Observations happen while the epoch is live.
2718
- if (now < epoch.endTimestamp)
2718
+ // Observations happen while the epoch is live. This is the dominant idle
2719
+ // window fold returned-name pruning in here so it isn't starved on
2720
+ // clusters whose epochs park here (e.g. imported gateways that can't
2721
+ // observe → distribution never advances → the post-distribution tail below
2722
+ // is never reached).
2723
+ if (now < epoch.endTimestamp) {
2724
+ if (enablePrune) {
2725
+ const pruned = await this.maybePruneReturnedNamesStep(opts, now);
2726
+ if (pruned)
2727
+ return pruned;
2728
+ }
2719
2729
  return { action: 'idle', reason: 'waiting_for_observations' };
2730
+ }
2720
2731
  // Distribute (batched).
2721
2732
  if (epoch.rewardsDistributed === 0) {
2722
2733
  const gatewayAccounts = epoch.activeGatewayCount > 0
@@ -2761,6 +2772,11 @@ export class SolanaARIOWriteable extends SolanaARIOReadable {
2761
2772
  if (rolled)
2762
2773
  return rolled;
2763
2774
  }
2775
+ if (enablePrune) {
2776
+ const pruned = await this.maybePruneReturnedNamesStep(opts, now);
2777
+ if (pruned)
2778
+ return pruned;
2779
+ }
2764
2780
  // Current epoch fully processed — create the next once its start arrives.
2765
2781
  if (now >= nextEpochStart) {
2766
2782
  const { id } = await this.createEpoch();
@@ -2805,6 +2821,38 @@ export class SolanaARIOWriteable extends SolanaARIOReadable {
2805
2821
  const { id } = await this.updateDemandFactor();
2806
2822
  return { action: 'update_demand_factor', txId: id };
2807
2823
  }
2824
+ /** Wall-clock (ms) of the last returned-name prune scan; throttles the
2825
+ * getProgramAccounts scan below the crank poll rate. */
2826
+ lastReturnedNamePruneScanMs = 0;
2827
+ /**
2828
+ * One prune batch over ReturnedName PDAs whose 14-day auction window has
2829
+ * elapsed (≤ {@link CrankEpochStepOptions.pruneBatchSize} per tx), or `null`
2830
+ * when none are due / the scan is throttled. Scans the PDAs directly — it does
2831
+ * NOT gate on `config.next_returned_names_prune_timestamp`, which is never set
2832
+ * for imported returned names and would strand them. The contract re-checks
2833
+ * each account's window, so a slightly-skewed client clock is safe.
2834
+ */
2835
+ async maybePruneReturnedNamesStep(opts, now) {
2836
+ const scanInterval = opts.pruneScanIntervalMs ?? 60_000;
2837
+ const wallNow = Date.now();
2838
+ if (wallNow - this.lastReturnedNamePruneScanMs < scanInterval)
2839
+ return null;
2840
+ this.lastReturnedNamePruneScanMs = wallNow;
2841
+ const expired = await this.getExpiredReturnedNames(now);
2842
+ if (expired.length === 0)
2843
+ return null;
2844
+ const batchSize = opts.pruneBatchSize ?? 15;
2845
+ const batch = expired.slice(0, batchSize).map((r) => r.pubkey);
2846
+ const { id } = await this.pruneReturnedNames({
2847
+ maxNames: batch.length,
2848
+ returnedNames: batch,
2849
+ });
2850
+ return {
2851
+ action: 'prune_returned_names',
2852
+ txId: id,
2853
+ progress: { index: batch.length, total: expired.length },
2854
+ };
2855
+ }
2808
2856
  /**
2809
2857
  * The DemandFactor account's stored period + period-zero start (seconds) —
2810
2858
  * the gate for {@link maybeRollDemandFactorStep}. `null` if the account
@@ -14,4 +14,4 @@
14
14
  * limitations under the License.
15
15
  */
16
16
  // AUTOMATICALLY GENERATED FILE - DO NOT TOUCH
17
- export const version = '4.0.0-solana.37';
17
+ export const version = '4.0.0-solana.38';
@@ -122,7 +122,7 @@ export declare function buildObservationBitmap(registryAddresses: string[], fail
122
122
  */
123
123
  export declare function encodeReportTxId(reportTxId: string | undefined): Buffer;
124
124
  /** The single on-chain action a {@link SolanaARIOWriteable.crankEpochStep} call performed. */
125
- export type CrankAction = 'create' | 'tally' | 'prescribe' | 'distribute' | 'compound' | 'update_demand_factor' | 'close' | 'idle';
125
+ export type CrankAction = 'create' | 'tally' | 'prescribe' | 'distribute' | 'compound' | 'update_demand_factor' | 'prune_returned_names' | 'close' | 'idle';
126
126
  /** Options for {@link SolanaARIOWriteable.crankEpochStep}. */
127
127
  export interface CrankEpochStepOptions {
128
128
  /** Gateways per tally/distribute batch. Default 30. */
@@ -158,6 +158,24 @@ export interface CrankEpochStepOptions {
158
158
  * between buys) current. Default true. Runs only in the idle tail.
159
159
  */
160
160
  enableDemandFactorRoll?: boolean;
161
+ /**
162
+ * Prune expired ReturnedName PDAs (auction window elapsed) as part of the
163
+ * crank. Default true. Runs whenever the epoch lifecycle is otherwise idle
164
+ * (the live observation window AND the post-distribution tail), one batch per
165
+ * step. It scans the ReturnedName PDAs DIRECTLY (via getExpiredReturnedNames)
166
+ * and does NOT consult `config.next_returned_names_prune_timestamp` — that
167
+ * hint is only set for on-chain returns and is never updated for imported
168
+ * returned names, so trusting it strands imported auctions forever.
169
+ */
170
+ enablePrune?: boolean;
171
+ /** ReturnedName PDAs to prune per tx (u8, max 255). Default 15. */
172
+ pruneBatchSize?: number;
173
+ /**
174
+ * Minimum wall-clock gap between returned-name prune SCANS (ms). The scan is a
175
+ * `getProgramAccounts` over ReturnedName PDAs, so this throttles it below the
176
+ * crank poll rate. Default 60_000 (1 min). Set 0 to scan every step (tests).
177
+ */
178
+ pruneScanIntervalMs?: number;
161
179
  /** Unix seconds; defaults to the wall clock. Injectable for testing. */
162
180
  now?: number;
163
181
  }
@@ -727,6 +745,18 @@ export declare class SolanaARIOWriteable extends SolanaARIOReadable {
727
745
  * itself is idempotent.
728
746
  */
729
747
  private maybeRollDemandFactorStep;
748
+ /** Wall-clock (ms) of the last returned-name prune scan; throttles the
749
+ * getProgramAccounts scan below the crank poll rate. */
750
+ private lastReturnedNamePruneScanMs;
751
+ /**
752
+ * One prune batch over ReturnedName PDAs whose 14-day auction window has
753
+ * elapsed (≤ {@link CrankEpochStepOptions.pruneBatchSize} per tx), or `null`
754
+ * when none are due / the scan is throttled. Scans the PDAs directly — it does
755
+ * NOT gate on `config.next_returned_names_prune_timestamp`, which is never set
756
+ * for imported returned names and would strand them. The contract re-checks
757
+ * each account's window, so a slightly-skewed client clock is safe.
758
+ */
759
+ private maybePruneReturnedNamesStep;
730
760
  /**
731
761
  * The DemandFactor account's stored period + period-zero start (seconds) —
732
762
  * the gate for {@link maybeRollDemandFactorStep}. `null` if the account
@@ -13,4 +13,4 @@
13
13
  * See the License for the specific language governing permissions and
14
14
  * limitations under the License.
15
15
  */
16
- export declare const version = "4.0.0-solana.36";
16
+ export declare const version = "4.0.0-solana.37";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ar.io/sdk",
3
- "version": "4.0.0-solana.37",
3
+ "version": "4.0.0-solana.38",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/ar-io/ar-io-sdk.git"