@ar.io/sdk 3.24.0 → 4.0.0-alpha.1

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 (169) hide show
  1. package/README.md +682 -600
  2. package/lib/esm/cli/cli.js +188 -152
  3. package/lib/esm/cli/commands/antCommands.js +23 -58
  4. package/lib/esm/cli/commands/arnsPurchaseCommands.js +48 -30
  5. package/lib/esm/cli/commands/escrowCommands.js +221 -0
  6. package/lib/esm/cli/commands/gatewayWriteCommands.js +142 -23
  7. package/lib/esm/cli/commands/pruneCommands.js +150 -0
  8. package/lib/esm/cli/commands/readCommands.js +22 -3
  9. package/lib/esm/cli/commands/transfer.js +6 -6
  10. package/lib/esm/cli/options.js +124 -58
  11. package/lib/esm/cli/utils.js +280 -174
  12. package/lib/esm/common/ant-registry.js +17 -143
  13. package/lib/esm/common/ant.js +44 -1167
  14. package/lib/esm/common/faucet.js +11 -6
  15. package/lib/esm/common/index.js +0 -4
  16. package/lib/esm/common/io.js +25 -1412
  17. package/lib/esm/constants.js +13 -19
  18. package/lib/esm/solana/ant-readable.js +724 -0
  19. package/lib/esm/solana/ant-registry-readable.js +133 -0
  20. package/lib/esm/solana/ant-registry-writeable.js +472 -0
  21. package/lib/esm/solana/ant-writeable.js +384 -0
  22. package/lib/esm/solana/ata.js +70 -0
  23. package/lib/esm/solana/canonical-message.js +128 -0
  24. package/lib/esm/solana/clusters.js +111 -0
  25. package/lib/esm/solana/constants.js +146 -0
  26. package/lib/esm/solana/delegation-math.js +112 -0
  27. package/lib/esm/solana/deserialize.js +711 -0
  28. package/lib/esm/solana/escrow.js +839 -0
  29. package/lib/{cjs/utils/json.js → esm/solana/events.js} +15 -10
  30. package/lib/esm/solana/funding-plan.js +699 -0
  31. package/lib/esm/solana/index.js +126 -0
  32. package/lib/esm/solana/instruction.js +39 -0
  33. package/lib/esm/solana/io-readable.js +2182 -0
  34. package/lib/esm/solana/io-writeable.js +3196 -0
  35. package/lib/esm/solana/json-rpc.js +90 -0
  36. package/lib/esm/solana/metadata.js +81 -0
  37. package/lib/esm/solana/mpl-core.js +192 -0
  38. package/lib/esm/solana/pda.js +332 -0
  39. package/lib/esm/solana/predict-prescribed-observers.js +110 -0
  40. package/lib/esm/solana/retry.js +117 -0
  41. package/lib/esm/solana/rpc-circuit-breaker.js +258 -0
  42. package/lib/esm/solana/send.js +372 -0
  43. package/lib/esm/solana/spawn-ant.js +224 -0
  44. package/lib/esm/solana/types.js +1 -0
  45. package/lib/esm/types/ant.js +27 -15
  46. package/lib/esm/types/io.js +8 -11
  47. package/lib/esm/utils/ant.js +0 -63
  48. package/lib/esm/utils/index.js +0 -3
  49. package/lib/esm/version.js +1 -1
  50. package/lib/types/cli/commands/antCommands.d.ts +5 -13
  51. package/lib/types/cli/commands/arnsPurchaseCommands.d.ts +33 -7
  52. package/lib/types/cli/commands/escrowCommands.d.ts +68 -0
  53. package/lib/types/cli/commands/gatewayWriteCommands.d.ts +12 -11
  54. package/lib/types/cli/commands/pruneCommands.d.ts +31 -0
  55. package/lib/types/cli/commands/readCommands.d.ts +27 -22
  56. package/lib/types/cli/commands/transfer.d.ts +9 -9
  57. package/lib/types/cli/options.d.ts +76 -21
  58. package/lib/types/cli/types.d.ts +11 -13
  59. package/lib/types/cli/utils.d.ts +71 -31
  60. package/lib/types/common/ant-registry.d.ts +49 -47
  61. package/lib/types/common/ant.d.ts +54 -539
  62. package/lib/types/common/faucet.d.ts +20 -8
  63. package/lib/types/common/index.d.ts +0 -3
  64. package/lib/types/common/io.d.ts +51 -263
  65. package/lib/types/constants.d.ts +11 -18
  66. package/lib/types/solana/ant-readable.d.ts +180 -0
  67. package/lib/types/solana/ant-registry-readable.d.ts +105 -0
  68. package/lib/types/solana/ant-registry-writeable.d.ts +249 -0
  69. package/lib/types/solana/ant-writeable.d.ts +177 -0
  70. package/lib/types/solana/ata.d.ts +44 -0
  71. package/lib/types/solana/canonical-message.d.ts +121 -0
  72. package/lib/types/solana/clusters.d.ts +109 -0
  73. package/lib/types/solana/constants.d.ts +119 -0
  74. package/lib/types/solana/delegation-math.d.ts +45 -0
  75. package/lib/types/solana/deserialize.d.ts +262 -0
  76. package/lib/types/solana/escrow.d.ts +480 -0
  77. package/lib/types/solana/events.d.ts +38 -0
  78. package/lib/types/solana/funding-plan.d.ts +225 -0
  79. package/lib/types/solana/index.d.ts +87 -0
  80. package/lib/types/solana/instruction.d.ts +39 -0
  81. package/lib/types/solana/io-readable.d.ts +499 -0
  82. package/lib/types/solana/io-writeable.d.ts +893 -0
  83. package/lib/types/solana/json-rpc.d.ts +47 -0
  84. package/lib/types/solana/metadata.d.ts +84 -0
  85. package/lib/types/solana/mpl-core.d.ts +120 -0
  86. package/lib/types/solana/pda.d.ts +95 -0
  87. package/lib/types/solana/predict-prescribed-observers.d.ts +28 -0
  88. package/lib/types/solana/retry.d.ts +62 -0
  89. package/lib/types/solana/rpc-circuit-breaker.d.ts +78 -0
  90. package/lib/types/solana/send.d.ts +94 -0
  91. package/lib/types/solana/spawn-ant.d.ts +145 -0
  92. package/lib/types/solana/types.d.ts +82 -0
  93. package/lib/types/types/ant-registry.d.ts +43 -4
  94. package/lib/types/types/ant.d.ts +114 -96
  95. package/lib/types/types/common.d.ts +18 -74
  96. package/lib/types/types/faucet.d.ts +2 -2
  97. package/lib/types/types/io.d.ts +244 -158
  98. package/lib/types/types/token.d.ts +0 -12
  99. package/lib/types/utils/ant.d.ts +1 -12
  100. package/lib/types/utils/index.d.ts +0 -3
  101. package/lib/types/version.d.ts +1 -1
  102. package/package.json +36 -33
  103. package/lib/cjs/cli/cli.js +0 -822
  104. package/lib/cjs/cli/commands/antCommands.js +0 -113
  105. package/lib/cjs/cli/commands/arnsPurchaseCommands.js +0 -212
  106. package/lib/cjs/cli/commands/gatewayWriteCommands.js +0 -210
  107. package/lib/cjs/cli/commands/readCommands.js +0 -215
  108. package/lib/cjs/cli/commands/transfer.js +0 -159
  109. package/lib/cjs/cli/options.js +0 -470
  110. package/lib/cjs/cli/types.js +0 -2
  111. package/lib/cjs/cli/utils.js +0 -639
  112. package/lib/cjs/common/ant-registry.js +0 -155
  113. package/lib/cjs/common/ant-versions.js +0 -93
  114. package/lib/cjs/common/ant.js +0 -1182
  115. package/lib/cjs/common/arweave.js +0 -27
  116. package/lib/cjs/common/contracts/ao-process.js +0 -224
  117. package/lib/cjs/common/error.js +0 -64
  118. package/lib/cjs/common/faucet.js +0 -150
  119. package/lib/cjs/common/hyperbeam/hb.js +0 -173
  120. package/lib/cjs/common/index.js +0 -42
  121. package/lib/cjs/common/io.js +0 -1423
  122. package/lib/cjs/common/logger.js +0 -83
  123. package/lib/cjs/common/loggers/winston.js +0 -68
  124. package/lib/cjs/common/marketplace.js +0 -731
  125. package/lib/cjs/common/turbo.js +0 -223
  126. package/lib/cjs/constants.js +0 -41
  127. package/lib/cjs/node/index.js +0 -39
  128. package/lib/cjs/package.json +0 -1
  129. package/lib/cjs/types/ant-registry.js +0 -2
  130. package/lib/cjs/types/ant.js +0 -168
  131. package/lib/cjs/types/common.js +0 -2
  132. package/lib/cjs/types/faucet.js +0 -2
  133. package/lib/cjs/types/index.js +0 -37
  134. package/lib/cjs/types/io.js +0 -51
  135. package/lib/cjs/types/token.js +0 -116
  136. package/lib/cjs/utils/ant.js +0 -108
  137. package/lib/cjs/utils/ao.js +0 -432
  138. package/lib/cjs/utils/arweave.js +0 -285
  139. package/lib/cjs/utils/base64.js +0 -62
  140. package/lib/cjs/utils/hash.js +0 -56
  141. package/lib/cjs/utils/index.js +0 -38
  142. package/lib/cjs/utils/processes.js +0 -173
  143. package/lib/cjs/utils/random.js +0 -30
  144. package/lib/cjs/utils/schema.js +0 -15
  145. package/lib/cjs/utils/url.js +0 -37
  146. package/lib/cjs/version.js +0 -20
  147. package/lib/cjs/web/index.js +0 -41
  148. package/lib/esm/common/ant-versions.js +0 -87
  149. package/lib/esm/common/arweave.js +0 -21
  150. package/lib/esm/common/contracts/ao-process.js +0 -220
  151. package/lib/esm/common/hyperbeam/hb.js +0 -169
  152. package/lib/esm/common/marketplace.js +0 -724
  153. package/lib/esm/common/turbo.js +0 -215
  154. package/lib/esm/node/index.js +0 -20
  155. package/lib/esm/utils/ao.js +0 -420
  156. package/lib/esm/utils/arweave.js +0 -271
  157. package/lib/esm/utils/processes.js +0 -167
  158. package/lib/esm/web/index.js +0 -20
  159. package/lib/types/common/ant-versions.d.ts +0 -39
  160. package/lib/types/common/arweave.d.ts +0 -17
  161. package/lib/types/common/contracts/ao-process.d.ts +0 -47
  162. package/lib/types/common/hyperbeam/hb.d.ts +0 -88
  163. package/lib/types/common/marketplace.d.ts +0 -568
  164. package/lib/types/common/turbo.d.ts +0 -61
  165. package/lib/types/node/index.d.ts +0 -20
  166. package/lib/types/utils/ao.d.ts +0 -80
  167. package/lib/types/utils/arweave.d.ts +0 -79
  168. package/lib/types/utils/processes.d.ts +0 -39
  169. package/lib/types/web/index.d.ts +0 -20
@@ -0,0 +1,893 @@
1
+ /**
2
+ * Copyright (C) 2022-2024 Permanent Data Solutions, Inc.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ /**
17
+ * Solana implementation of ARIOWrite interface.
18
+ *
19
+ * Extends SolanaARIOReadable with write operations that build and send
20
+ * Solana transactions via Codama-generated instruction builders.
21
+ *
22
+ * All instruction encoding (discriminators, account ordering, Borsh codecs,
23
+ * default value resolution for token/system programs) is delegated to the
24
+ * generated builders in `./generated/{core,gar,arns}/instructions/`. The
25
+ * builders are derived from the on-chain IDL and stay in sync via codegen.
26
+ *
27
+ * This file's job is just to:
28
+ * 1. Translate the AO-style SDK params into the builder's input shape.
29
+ * 2. Pre-derive the PDAs that the *Async builders can't infer (the ones
30
+ * whose seeds depend on runtime state — e.g. the next withdrawal id
31
+ * from the on-chain counter, or the buyer's ATA from a runtime mint).
32
+ * 3. Append remaining_accounts (gateway PDAs, name registry) for the
33
+ * epoch crank instructions, since Codama doesn't generate a typed
34
+ * surface for them.
35
+ */
36
+ import { type Address, type Instruction } from '@solana/kit';
37
+ import type { ILogger } from '../common/logger.js';
38
+ import type { MessageResult, WriteOptions } from '../types/common.js';
39
+ import type { ArNSPurchaseParams, BuyRecordParams, CreateVaultParams, DelegateStakeParams, ExtendLeaseParams, ExtendVaultParams, IncreaseUndernameLimitParams, IncreaseVaultParams, JoinNetworkParams, RedelegateStakeParams, RevokeVaultParams, UpdateGatewaySettingsParams, VaultedTransferParams } from '../types/io.js';
40
+ import type { mARIOToken } from '../types/token.js';
41
+ import { deserializeEpochSettingsFull } from './deserialize.js';
42
+ import { SolanaARIOReadable } from './io-readable.js';
43
+ import type { SolanaRpcSubscriptions, SolanaSigner, SolanaWriteConfig } from './types.js';
44
+ /**
45
+ * Pick the swapped-gateway operator that `finalize_gone` needs as a writable
46
+ * `remaining_accounts[0]`.
47
+ *
48
+ * `finalize_gone` reclaims a gateway's slot from the compact GatewayRegistry by
49
+ * moving the LAST active slot into it and rewriting that swapped gateway's
50
+ * stored `registry_index`. When the finalized gateway is NOT already the last
51
+ * slot, the on-chain handler requires the swapped Gateway PDA (writable) at
52
+ * `remaining_accounts[0]`; when it IS the last slot, no swap occurs and no
53
+ * extra account is needed. See
54
+ * `programs/ario-gar/src/instructions/gateway.rs::finalize_gone`.
55
+ *
56
+ * `registryAddresses` MUST be the active registry operator addresses in slot
57
+ * order (`getRegistryGatewayAddresses()` — length === on-chain
58
+ * `registry.count`), so `registryAddresses[length - 1]` is exactly the
59
+ * `registry.gateways[count - 1].address` the on-chain swap reads.
60
+ *
61
+ * @returns the swapped gateway's operator address, or `null` when the finalized
62
+ * gateway already occupies the last slot.
63
+ * @throws if `registryIndex` is outside the active registry count (mirrors the
64
+ * on-chain `index < registry.count` guard, surfacing a stale index early).
65
+ */
66
+ export declare function selectFinalizeGoneSwapOperator(registryIndex: number, registryAddresses: string[]): string | null;
67
+ /**
68
+ * Split a primary name into its undername + base parts using the same rule
69
+ * as the on-chain `splitn(2, '_')` in `programs/ario-core/src/instructions/primary_name.rs`:
70
+ * everything before the first '_' is the undername, the rest is the base.
71
+ *
72
+ * Exposed as a top-level helper so it can be unit-tested without spinning up
73
+ * an `SolanaARIOWriteable`. Lowercases the input to match contract behavior.
74
+ */
75
+ export declare function splitPrimaryName(name: string): {
76
+ isUndername: boolean;
77
+ baseName: string;
78
+ undername: string | null;
79
+ };
80
+ /**
81
+ * Solana-backed read-write client for the AR.IO protocol.
82
+ *
83
+ * Usage:
84
+ * ```ts
85
+ * import {
86
+ * createSolanaRpc,
87
+ * createSolanaRpcSubscriptions,
88
+ * generateKeyPairSigner,
89
+ * } from '@solana/kit';
90
+ * import { SolanaARIOWriteable } from '@ar.io/sdk/solana';
91
+ *
92
+ * const rpc = createSolanaRpc('https://api.mainnet-beta.solana.com');
93
+ * const rpcSubscriptions = createSolanaRpcSubscriptions('wss://api.mainnet-beta.solana.com');
94
+ * const signer = await generateKeyPairSigner();
95
+ * const ario = new SolanaARIOWriteable({ rpc, rpcSubscriptions, signer });
96
+ *
97
+ * await ario.transfer({ target: 'RecipientPubkey...', qty: 100_000_000 });
98
+ * ```
99
+ */
100
+ /** Build the gateway_results bitmap for save_observations.
101
+ * All bits start as 1 (pass) for the first `registryAddresses.length`
102
+ * positions; positions named in `failedGateways` get cleared to 0; all
103
+ * positions beyond `registryAddresses.length` are 0. */
104
+ export declare function buildObservationBitmap(registryAddresses: string[], failedGateways: string[]): Buffer;
105
+ /** Encode an Arweave TX ID into the on-chain `[u8; 32]` slot.
106
+ *
107
+ * An Arweave TX ID **is** a 32-byte SHA-256 hash; the 43-char base64url
108
+ * string is just its presentation encoding. We decode here so the
109
+ * on-chain bytes are the raw hash — lossless and trivially reversible
110
+ * via base64url-encode on the consumer side. Without this, on-chain
111
+ * bytes alone couldn't be used to look up the original report bundle
112
+ * on permaweb (the whole point of recording the txid for auditability).
113
+ *
114
+ * Empty / undefined input → 32 zero bytes ("no permaweb archive
115
+ * configured for this submission" — the report still lives off-chain
116
+ * in the observer's local sinks but isn't anchored on Arweave).
117
+ *
118
+ * Throws on malformed input: the base64url string must be exactly 43
119
+ * chars and decode to 32 bytes. Strict validation here is desirable —
120
+ * silently truncating or accepting bad input would erode the
121
+ * auditability that the field exists for.
122
+ */
123
+ export declare function encodeReportTxId(reportTxId: string | undefined): Buffer;
124
+ /** The single on-chain action a {@link SolanaARIOWriteable.crankEpochStep} call performed. */
125
+ export type CrankAction = 'create' | 'tally' | 'prescribe' | 'distribute' | 'compound' | 'update_demand_factor' | 'prune_returned_names' | 'close_observation' | 'close' | 'idle';
126
+ /** Options for {@link SolanaARIOWriteable.crankEpochStep}. */
127
+ export interface CrankEpochStepOptions {
128
+ /** Gateways per tally/distribute batch. Default 30. */
129
+ batchSize?: number;
130
+ /**
131
+ * NameRegistry account for the name-prescription leg. Defaults to the
132
+ * registry derived from the configured ArNS program. Pass `null` to disable
133
+ * name prescription entirely.
134
+ */
135
+ nameRegistryAccount?: Address | null;
136
+ /** Close fully-distributed epochs older than `epochRetention`. Default true. */
137
+ enableClose?: boolean;
138
+ /** Epochs of retention before an epoch may be closed (GAR-006). Default 7. */
139
+ epochRetention?: number;
140
+ /**
141
+ * Compound pending delegate rewards (settle the reward-per-share accumulator
142
+ * into delegated stake) once the live epoch is fully distributed, so the next
143
+ * epoch's tally weights the compounded stake. Default true. The delegate
144
+ * rewards are correct in the accumulator regardless — this only materializes
145
+ * them on-chain. Each step compounds one batch; runs only in the otherwise-
146
+ * idle tail (never during tally/distribute).
147
+ */
148
+ enableCompound?: boolean;
149
+ /**
150
+ * Skip compounding delegations whose pending reward is at/below this (mARIO).
151
+ * Avoids dust compounds that only advance `reward_debt`. Default 0.
152
+ */
153
+ compoundMinPendingRewards?: number;
154
+ /**
155
+ * Roll the demand factor forward when its (wall-clock) period has elapsed.
156
+ * Idempotent — only sends a tx when the stored period is behind. Pricing is
157
+ * always lazily correct without this; it keeps the STORED factor (and reads
158
+ * between buys) current. Default true. Runs only in the idle tail.
159
+ */
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;
179
+ /** Unix seconds; defaults to the wall clock. Injectable for testing. */
180
+ now?: number;
181
+ }
182
+ /** Result of a single {@link SolanaARIOWriteable.crankEpochStep} call. */
183
+ export interface CrankEpochStepResult {
184
+ /** The action performed (or `'idle'` when nothing was due). */
185
+ action: CrankAction;
186
+ /** The epoch the action targeted (absent for `'idle'`). */
187
+ epochIndex?: number;
188
+ /** Confirmed transaction signature, when an action was submitted. */
189
+ txId?: string;
190
+ /** Batch progress for `'tally'` / `'distribute'`. */
191
+ progress?: {
192
+ index: number;
193
+ total: number;
194
+ };
195
+ /** For `action: 'idle'`, why nothing was done. */
196
+ reason?: 'epochs_disabled' | 'waiting_for_genesis' | 'waiting_for_epoch' | 'waiting_for_observations' | 'epoch_complete';
197
+ }
198
+ /**
199
+ * Detect the GAR `InvalidGatewayAccount` error by Anchor error name/message
200
+ * (walking the cause chain + `context.logs`), NOT by numeric code — codes are
201
+ * `6000 + enum-index` and shift across program versions, but the name and
202
+ * message are stable. `prescribe_epoch` raises this when a supplied observer
203
+ * Gateway PDA is missing/spoofed (e.g. a predicted observer left the registry
204
+ * between prediction and tx landing).
205
+ */
206
+ export declare function isInvalidGatewayAccountError(error: unknown): boolean;
207
+ export declare class SolanaARIOWriteable extends SolanaARIOReadable {
208
+ protected readonly signer: SolanaSigner;
209
+ protected readonly rpcSubscriptions: SolanaRpcSubscriptions;
210
+ constructor(config: SolanaWriteConfig & {
211
+ logger?: ILogger;
212
+ coreProgramId?: Address;
213
+ garProgramId?: Address;
214
+ arnsProgramId?: Address;
215
+ antProgramId?: Address;
216
+ });
217
+ /** The signer's on-chain address. */
218
+ protected get signerAddress(): Address;
219
+ protected sendTransaction(instructions: Instruction[], computeUnitLimit?: number): Promise<string>;
220
+ /** Helper to get the ARIO mint and treasury from ArioConfig */
221
+ private getCoreConfig;
222
+ private getMint;
223
+ /** Helper to get ArNS config fields (mint and treasury) */
224
+ private getArnsConfig;
225
+ /** Helper to get GAR config fields (mint, stake pool, protocol pool) */
226
+ private getGarConfig;
227
+ /**
228
+ * Inject ARNS default PDAs (config, demandFactor, nameRegistry).
229
+ *
230
+ * Extra fields not consumed by a given builder are harmlessly ignored
231
+ * (codama only reads the named keys from `input`).
232
+ */
233
+ private withArnsDefaults;
234
+ /**
235
+ * If the on-chain ArnsRecord for `name` hasn't been migrated to the
236
+ * current schema (name_hash at offset 8 doesn't match the expected
237
+ * hash), return a `migrate_arns_record` instruction that must be
238
+ * prepended to any operation referencing the record with PDA seed
239
+ * verification.
240
+ *
241
+ * Returns an empty array when the record is already up-to-date or
242
+ * doesn't exist.
243
+ */
244
+ private _buildMigrateArnsRecordIxIfNeeded;
245
+ /** Inject ARIO core default PDAs (config). */
246
+ private withCoreDefaults;
247
+ /** Inject GAR default PDAs (settings, epochSettings, registry). */
248
+ private withGarDefaults;
249
+ /** Read WithdrawalCounter's next_id (returns 0n if not yet created) */
250
+ private getNextWithdrawalId;
251
+ transfer(params: {
252
+ target: string;
253
+ qty: number | mARIOToken;
254
+ }, _options?: WriteOptions): Promise<MessageResult>;
255
+ vaultedTransfer(params: VaultedTransferParams, _options?: WriteOptions): Promise<MessageResult>;
256
+ createVault(params: CreateVaultParams, _options?: WriteOptions): Promise<MessageResult>;
257
+ /** Read VaultCounter's next_id (returns 0n if not yet created). */
258
+ private getNextVaultId;
259
+ extendVault(params: ExtendVaultParams, _options?: WriteOptions): Promise<MessageResult>;
260
+ increaseVault(params: IncreaseVaultParams, _options?: WriteOptions): Promise<MessageResult>;
261
+ revokeVault(params: RevokeVaultParams, _options?: WriteOptions): Promise<MessageResult>;
262
+ joinNetwork(params: JoinNetworkParams, _options?: WriteOptions): Promise<MessageResult>;
263
+ leaveNetwork(_options?: WriteOptions): Promise<MessageResult>;
264
+ updateGatewaySettings(params: UpdateGatewaySettingsParams, _options?: WriteOptions): Promise<MessageResult>;
265
+ increaseOperatorStake(params: {
266
+ increaseQty: number | mARIOToken;
267
+ }, _options?: WriteOptions): Promise<MessageResult>;
268
+ decreaseOperatorStake(params: {
269
+ decreaseQty: number | mARIOToken;
270
+ instant?: boolean;
271
+ }, _options?: WriteOptions): Promise<MessageResult>;
272
+ delegateStake(params: DelegateStakeParams, _options?: WriteOptions): Promise<MessageResult>;
273
+ decreaseDelegateStake(params: {
274
+ target: string;
275
+ decreaseQty: number | mARIOToken;
276
+ instant?: boolean;
277
+ }, _options?: WriteOptions): Promise<MessageResult>;
278
+ instantWithdrawal(params: {
279
+ gatewayAddress?: string;
280
+ vaultId: string;
281
+ }, _options?: WriteOptions): Promise<MessageResult>;
282
+ cancelWithdrawal(params: {
283
+ gatewayAddress?: string;
284
+ vaultId: string;
285
+ }, _options?: WriteOptions): Promise<MessageResult>;
286
+ saveObservations(params: {
287
+ reportTxId: string;
288
+ failedGateways: string[];
289
+ epochIndex?: number;
290
+ /** Raw 256-byte gateway results bitfield (if provided, overrides failedGateways) */
291
+ gatewayResults?: Uint8Array;
292
+ gatewayCount?: number;
293
+ }, _options?: WriteOptions): Promise<MessageResult>;
294
+ redelegateStake(params: RedelegateStakeParams, _options?: WriteOptions): Promise<MessageResult>;
295
+ buyRecord(params: BuyRecordParams, _options?: WriteOptions): Promise<MessageResult>;
296
+ /**
297
+ * Resolve a `FundingPlan` for a fee-paying ArNS ix. When `params.sources`
298
+ * is set, use it verbatim (caller-supplied plan); otherwise discover the
299
+ * user's sources and build a plan via the Lua-faithful planner.
300
+ *
301
+ * Throws `InsufficientFundingError` (as a thrown Error with the structured
302
+ * payload as `cause`) when no plan covers `amountNeeded`.
303
+ */
304
+ private _resolveFundingPlan;
305
+ /**
306
+ * Build a `buy_name_from_funding_plan` ix using the funding-plan module.
307
+ * Resolves per-source PDAs (Delegation, Withdrawal) and the residue-vault
308
+ * PDA prediction in one shot.
309
+ */
310
+ private _buildBuyNameFromFundingPlanIx;
311
+ /**
312
+ * For an explicit caller-supplied plan, detect which Delegation sources
313
+ * will trigger an on-chain residue auto-vault. Reads each (Delegation,
314
+ * Gateway) pair in parallel; computes post-drain; flags `(0, min)`.
315
+ *
316
+ * Hard-fails on RPC error or missing PDA — silently skipping would let
317
+ * the on-chain handler reject the tx with `MissingResidueVault` and
318
+ * burn fees. The error message points at remediation.
319
+ */
320
+ private _detectResidueIndexes;
321
+ /**
322
+ * Materialize a `FundingPlan` into the on-chain ix's per-source remaining
323
+ * accounts, residue-vault PDAs, and the withdrawal_counter slot. Shared
324
+ * across all 5 ArNS funding-plan ix dispatches and the 2 ario-core
325
+ * primary-name funding-plan dispatches.
326
+ *
327
+ * Throws when the plan has Withdrawal sources whose ids cannot be
328
+ * resolved from either `spec.withdrawalId` (preferred) or
329
+ * `params.withdrawalId` (single-withdrawal back-compat).
330
+ */
331
+ private _materializeFundingPlan;
332
+ /**
333
+ * Simulate the on-chain `get_token_cost` instruction and return the exact
334
+ * cost as a `bigint` (mARIO). This guarantees byte-exact agreement with the
335
+ * on-chain pricing math, avoiding integer-division rounding divergences
336
+ * that plagued the previous client-side reimplementation.
337
+ *
338
+ * The program writes a LE-u64 cost into the transaction's return data;
339
+ * we parse it from the simulation result.
340
+ */
341
+ private _simulateTokenCost;
342
+ private arnsConfigPda;
343
+ private demandFactorPda;
344
+ private nameRegistryPda;
345
+ upgradeRecord(params: ArNSPurchaseParams, _options?: WriteOptions): Promise<MessageResult>;
346
+ syncAttributes(params: {
347
+ name: string;
348
+ }, _options?: WriteOptions): Promise<MessageResult>;
349
+ /**
350
+ * Build a `sync_attributes` instruction for `name` IFF the signer is
351
+ * the current MPL Core asset owner. Returns `null` otherwise.
352
+ *
353
+ * Sprint 4 / ADR-016: bundle helper for `buyRecord`, `upgradeRecord`,
354
+ * `increaseUndernameLimit`, `reassignName`, and `buyReturnedName`.
355
+ * Returning `null` lets the caller send the arns ix alone — preserves
356
+ * BD-095 (non-holder ArNS lease management) and BD-096 (deferred
357
+ * trait sync for non-holder buyers). The actual ANT owner reconciles
358
+ * state later via the public `syncAttributes()`.
359
+ *
360
+ * `extendLease` is NOT a caller of this helper — extend_lease changes
361
+ * only `end_timestamp`, which isn't mirrored in any Attributes-plugin
362
+ * trait (BD-095 last row). `releaseName` is also excluded — release_name
363
+ * closes the ArnsRecord PDA, so a follow-up `sync_attributes` would
364
+ * fail the PDA-existence check.
365
+ *
366
+ * `assetOverride` MUST be set when the bundling ix mutates
367
+ * `record.ant` mid-tx (i.e. `reassign_name`). The on-chain record
368
+ * still points at the OLD asset at SDK build time, so without the
369
+ * override the bundled sync would target the wrong asset and fail
370
+ * the post-reassign `record.ant == asset.key()` check. The owner
371
+ * check runs against the supplied asset (= new ANT for reassign),
372
+ * matching the pre-reshape "new owner reconciles later" semantic.
373
+ */
374
+ private _buildSyncAttributesIxIfOwner;
375
+ /**
376
+ * Build a `sync_attributes` instruction unconditionally (no owner
377
+ * check). Used by the public `syncAttributes()` method, where the
378
+ * caller is asking explicitly — if they aren't the owner the chain
379
+ * returns NotNftHolder.
380
+ */
381
+ private _buildSyncAttributesIxUnconditional;
382
+ /** Pure builder; no RPC. Used by both gated + unconditional helpers. */
383
+ private _buildSyncAttributesIxFor;
384
+ extendLease(params: ExtendLeaseParams, _options?: WriteOptions): Promise<MessageResult>;
385
+ increaseUndernameLimit(params: IncreaseUndernameLimitParams, _options?: WriteOptions): Promise<MessageResult>;
386
+ /**
387
+ * Shared dispatch helper for the 3 manage variants (upgrade / extend /
388
+ * increaseUndername). They share the same account shape per the
389
+ * `manage_from_delegation_accounts!` / `manage_from_operator_stake_accounts!`
390
+ * macros in `programs/ario-arns/src/instructions/manage_from_stake.rs`.
391
+ * Only the operation-specific extra args differ (years for extend,
392
+ * quantity for increaseUndername).
393
+ */
394
+ private _buildManageStakeIx;
395
+ /**
396
+ * If the signer already has a primary name set, build the instruction(s)
397
+ * needed to remove it so they can be prepended to a request/set tx —
398
+ * enabling single-tx "change primary name" flows.
399
+ *
400
+ * Returns an empty array when the signer has no existing primary name.
401
+ *
402
+ * Throws when the signer has a legacy primary-name state (forward
403
+ * `PrimaryName` PDA exists but its paired `PrimaryNameReverse` PDA does
404
+ * NOT). Both `remove_primary_name` AND `request_and_set_primary_name`
405
+ * require the reverse PDA on-chain — the latter rejects with
406
+ * `MustRemoveExistingPrimaryName` (0x1786, code 6022) any time a
407
+ * forward record already exists for the signer, regardless of reverse
408
+ * state. Silently skipping the remove would queue a tx guaranteed to
409
+ * fail with that opaque error. Surfacing it at the client with a clear
410
+ * remediation pointer is the only safe behavior.
411
+ *
412
+ * The legacy state should not exist on any cluster post-snapshot/import
413
+ * PR #159 (which emits PrimaryNameReverse in lockstep with PrimaryName)
414
+ * — it's a relic of pre-#159 imports. Operators on affected clusters
415
+ * must run `yarn workspace @ar-io/migration-import backfill:primary-name-reverse`
416
+ * (in the solana-ar-io repo) before this method can succeed.
417
+ */
418
+ private _buildRemoveExistingPrimaryNameIxs;
419
+ /**
420
+ * Build the `remaining_accounts` slice + the `antProgramId` arg the
421
+ * four ario-core primary-name instructions consume. Sprint 2/5
422
+ * reshape (ADR-016): ario-core no longer reads MPL Core asset bytes.
423
+ * Authorization is "caller is the effective AntRecord owner for this
424
+ * name", resolved from the `AntRecord` + `AntConfig` PDAs (freshness-
425
+ * gated against `AntConfig.last_known_owner` — see ario-core BD-097 /
426
+ * BD-109). Both are program-PDA-pinned lookups.
427
+ *
428
+ * Layouts the on-chain handlers expect:
429
+ * request_primary_name: [arnsRecord, demandFactor]
430
+ * request_and_set_primary_name: [arnsRecord, demandFactor, antRecord, antConfig]
431
+ * approve_primary_name: [arnsRecord, antRecord, antConfig]
432
+ * remove_primary_name_for_base_name: [arnsRecord, antRecord(@), antConfig]
433
+ *
434
+ * `antRecord` keys off the undername part for undernames (e.g.
435
+ * "blog_arweave" → AntRecord at "blog") or the canonical "@" sentinel
436
+ * for base names. `removeForBaseName` is special — it always uses "@"
437
+ * regardless of whether the primary name being removed is an undername,
438
+ * since the *base* name owner is the one revoking it.
439
+ *
440
+ * `antProgram` honors ADR-016 / BD-100 pluggability: the asset's
441
+ * `ANT Program` Attributes-plugin trait selects which program owns the
442
+ * AntRecord PDA. Absent / unparseable → canonical fallback. The detected
443
+ * trait is untrusted asset/RPC data, so it is honored only when it matches
444
+ * the canonical program or this client's explicitly-configured
445
+ * `this.antProgram`; any other value falls back to the configured program
446
+ * (see the SECURITY note in the body). Both the PDA derivation here and the
447
+ * `ant_program_id` arg the caller passes to the on-chain ix MUST agree (the
448
+ * handler re-derives and rejects mismatches).
449
+ */
450
+ private _buildPrimaryNameValidationAccounts;
451
+ requestPrimaryName(params: ArNSPurchaseParams, _options?: WriteOptions): Promise<MessageResult>;
452
+ setPrimaryName(params: ArNSPurchaseParams, _options?: WriteOptions): Promise<MessageResult>;
453
+ /**
454
+ * Build a `request_primary_name_from_funding_plan` or
455
+ * `request_and_set_primary_name_from_funding_plan` ix. Forwards both:
456
+ * - validation accounts (ArnsRecord + DemandFactor [+ ant_asset
457
+ * [+ AntRecord]]) — passed via remaining_accounts at indices
458
+ * [0..validation_account_count)
459
+ * - per-source PDAs from the funding plan — passed at indices
460
+ * [validation_account_count..)
461
+ *
462
+ * The on-chain handler (programs/ario-core/src/instructions/primary_name.rs)
463
+ * splits remaining_accounts at `validation_account_count` and forwards the
464
+ * funding-source slice to ario-gar's pay_from_funding_plan via CPI.
465
+ */
466
+ private _buildPrimaryNameFromFundingPlanIx;
467
+ private _coreConfigPda;
468
+ /**
469
+ * Approve a previously-created primary name request. The signer must be
470
+ * the AntRecord.owner for the requested name (undername part for
471
+ * undernames, "@" for base names) — Sprint 2 / ADR-016 reshape.
472
+ *
473
+ * Mirrors the on-chain `approve_primary_name` instruction
474
+ * (`programs/ario-core/src/instructions/primary_name.rs`).
475
+ * remaining_accounts: [arns_record(base), ant_record(undername | @), ant_config].
476
+ */
477
+ approvePrimaryName(params: {
478
+ initiator: Address;
479
+ name: string;
480
+ }, _options?: WriteOptions): Promise<MessageResult>;
481
+ /** Release tokens from an unlocked vault back to the owner. */
482
+ releaseVault(params: {
483
+ vaultId: string;
484
+ }, _options?: WriteOptions): Promise<MessageResult>;
485
+ /** Close an expired primary name request (permissionless — anyone can call). */
486
+ closeExpiredRequest(params: {
487
+ initiator: string;
488
+ }, _options?: WriteOptions): Promise<MessageResult>;
489
+ /** Claim tokens from a completed withdrawal (after lock period). */
490
+ claimWithdrawal(params: {
491
+ withdrawalId: string;
492
+ }, _options?: WriteOptions): Promise<MessageResult>;
493
+ /** Claim delegated stake from a gateway that is leaving the network. */
494
+ claimDelegateFromLeavingGateway(params: {
495
+ gatewayAddress: string;
496
+ }, _options?: WriteOptions): Promise<MessageResult>;
497
+ /**
498
+ * Claim a delegate's stake out of a gateway that has DISABLED delegation
499
+ * (`allow_delegated_staking == false`), moving it into the delegate's own
500
+ * withdrawal vault (WP §6.3 / Fix #6). This is the disabled-gateway analog of
501
+ * {@link claimDelegateFromLeavingGateway}: the on-chain instruction is
502
+ * permissionless, so a cranker can sweep delegates out (the operator cannot
503
+ * re-enable delegation until `total_delegated_stake == 0` and the cooldown
504
+ * elapses). The withdrawal-counter and withdrawal PDAs are seeded by the
505
+ * DELEGATOR, so a cranker must pass that delegate's `delegatorAddress`.
506
+ *
507
+ * @param params.gatewayAddress The gateway whose delegation was disabled.
508
+ * @param params.delegatorAddress The delegate to claim for. Defaults to the
509
+ * signer (self-claim). Pass another address to crank on a delegate's behalf;
510
+ * the signer covers rent (`payer`) but stake still routes to the delegate's
511
+ * own vault (the delegator key is bound by the delegation PDA seeds).
512
+ */
513
+ claimDelegateFromDisabledGateway(params: {
514
+ gatewayAddress: string;
515
+ delegatorAddress?: string;
516
+ }, _options?: WriteOptions): Promise<MessageResult>;
517
+ /** Add an address to the gateway's delegation allowlist. */
518
+ allowDelegate(params: {
519
+ delegate: string;
520
+ }, _options?: WriteOptions): Promise<MessageResult>;
521
+ /** Remove an address from the gateway's delegation allowlist. */
522
+ disallowDelegate(params: {
523
+ delegate: string;
524
+ }, _options?: WriteOptions): Promise<MessageResult>;
525
+ /** Enable or disable the delegation allowlist for the gateway. */
526
+ setAllowlistEnabled(params: {
527
+ enabled: boolean;
528
+ }, _options?: WriteOptions): Promise<MessageResult>;
529
+ /**
530
+ * Buy a name from the returned name auction (Dutch auction with premium).
531
+ *
532
+ * Phase 4: now dispatches on `params.fundFrom`. Note that for
533
+ * `buyReturnedName`, only the protocol share funds from the chosen source;
534
+ * the initiator share is always a direct buyer-ATA → initiator-ATA SPL
535
+ * transfer (matches the on-chain `_from_*` variant behavior).
536
+ */
537
+ buyReturnedName(params: {
538
+ name: string;
539
+ type: 'lease' | 'permabuy';
540
+ years?: number;
541
+ processId: string;
542
+ } & Partial<ArNSPurchaseParams>, _options?: WriteOptions): Promise<MessageResult>;
543
+ /**
544
+ * Pick a single stake-derived funding source that can cover a returned-name
545
+ * purchase, for the single-source `buy_returned_name_from_*` paths.
546
+ *
547
+ * Returned-name prices decay per slot, so the multi-source funding plan
548
+ * (which pre-commits exact amounts) can't match the execution-time cost. The
549
+ * single-source paths carry no amount — the program draws the live cost — so
550
+ * we only need to pick ONE source with enough stake. We size the pick against
551
+ * the premium-inclusive estimate (an upper bound, since the price only falls
552
+ * from now) and choose the largest matching source. Returns `null` when no
553
+ * single source covers the estimate.
554
+ */
555
+ private _autoPickReturnedNameStakeSource;
556
+ /** Reassign an ArNS name to a different ANT. */
557
+ reassignName(params: {
558
+ name: string;
559
+ processId: string;
560
+ }, _options?: WriteOptions): Promise<MessageResult>;
561
+ /** Release a permabuy name back to the registry (creates a returned name auction). */
562
+ releaseName(params: {
563
+ name: string;
564
+ }, _options?: WriteOptions): Promise<MessageResult>;
565
+ /**
566
+ * Roll the demand factor forward to the current period. Permissionless and
567
+ * idempotent — a no-op within the same period. Pricing already rolls the
568
+ * factor inline on every buy/extend, so this only refreshes the STORED
569
+ * factor that `getDemandFactor` and between-buy price previews read; a
570
+ * periodic crank (~once per 24h `PERIOD_LENGTH_SECONDS`) keeps it current.
571
+ */
572
+ updateDemandFactor(_options?: WriteOptions): Promise<MessageResult>;
573
+ /**
574
+ * Materialize a single delegate's pending rewards into their delegated
575
+ * stake by settling the gateway's reward-per-share accumulator.
576
+ * Permissionless — there is no signer beyond the fee payer; `delegator` is
577
+ * only a PDA-derivation seed. Rewards always accrue correctly in the
578
+ * accumulator regardless of this call; compounding makes the on-chain
579
+ * `delegatedStake` reflect them (and earn compound interest in the next
580
+ * epoch's weighting). Idempotent — a no-op once already settled.
581
+ */
582
+ compoundDelegationRewards(params: {
583
+ gateway: string;
584
+ delegator: string;
585
+ }, _options?: WriteOptions): Promise<MessageResult>;
586
+ /**
587
+ * Compound many delegates' rewards in a SINGLE transaction — one
588
+ * `compound_delegation_rewards` instruction per entry. Idempotent and
589
+ * permissionless, so partial batches are safe to retry. Keep each batch
590
+ * within the per-tx account/CU budget; grouping entries that share a gateway
591
+ * lowers the unique-account count (the gateway account is reused across
592
+ * instructions). Typical cranker usage: enumerate with
593
+ * `SolanaARIOReadable.getDelegationsToCompound`, chunk, then call this.
594
+ */
595
+ compoundDelegationRewardsBatch(delegations: Array<{
596
+ gateway: string;
597
+ delegator: string;
598
+ }>, _options?: WriteOptions): Promise<MessageResult>;
599
+ /**
600
+ * Build a single `compound_delegation_rewards` instruction (shared by the
601
+ * single + batch methods). PDAs are derived under the configured gar program
602
+ * so the program-id override always targets the right cluster.
603
+ */
604
+ private buildCompoundDelegationRewardsInstruction;
605
+ /**
606
+ * Create a new epoch. Permissionless — anyone can call when the next
607
+ * epoch's start time has arrived.
608
+ */
609
+ createEpoch(_options?: WriteOptions): Promise<MessageResult>;
610
+ /**
611
+ * Tally weights for a batch of gateways. Permissionless — call repeatedly
612
+ * until all gateways are processed. Pass gateway PDAs as
613
+ * `gatewayAccounts`; they're appended as `remaining_accounts`.
614
+ */
615
+ tallyWeights(params: {
616
+ epochIndex: number;
617
+ gatewayAccounts: Address[];
618
+ }, _options?: WriteOptions): Promise<MessageResult>;
619
+ /**
620
+ * Prescribe observers and names for an epoch. Permissionless — call after
621
+ * weights are tallied.
622
+ *
623
+ * `gatewayAccounts` MUST be the Gateway PDAs of the SELECTED observers only
624
+ * — at most `epoch_settings.prescribed_observer_count` (≤50), NOT the whole
625
+ * registry. The selection is computed on-chain; mirror it off-chain with
626
+ * {@link predictPrescribedObservers} / {@link getPredictedObserverPDAs} to
627
+ * learn the set. Passing every registry gateway (e.g. via
628
+ * {@link getAllRegistryGatewayPDAs}) hits Solana's `MAX_TX_ACCOUNT_LOCKS = 64`
629
+ * on large registries and the tx fails at pre-flight.
630
+ *
631
+ * The selected PDAs are appended as `remaining_accounts`, followed by the
632
+ * optional `nameRegistryAccount` (must be LAST) which enables the name
633
+ * prescription leg.
634
+ *
635
+ * If a selected gateway leaves between prediction and tx landing, the tx
636
+ * fails with `InvalidGatewayAccount` — retry once with a fresh prediction.
637
+ */
638
+ prescribeEpoch(params: {
639
+ epochIndex: number;
640
+ gatewayAccounts: Address[];
641
+ /** Optional NameRegistry account — pass to enable name prescription */
642
+ nameRegistryAccount?: Address;
643
+ }, _options?: WriteOptions): Promise<MessageResult>;
644
+ /**
645
+ * Distribute rewards for a completed epoch in batches. Permissionless —
646
+ * call after epoch ends. Gateway PDAs appended as `remaining_accounts`.
647
+ */
648
+ distributeEpoch(params: {
649
+ epochIndex: number;
650
+ gatewayAccounts: Address[];
651
+ }, _options?: WriteOptions): Promise<MessageResult>;
652
+ /**
653
+ * Close an old epoch account and reclaim rent. Permissionless — call after
654
+ * the epoch is distributed and at least 7 epochs have passed.
655
+ */
656
+ closeEpoch(params: {
657
+ epochIndex: number;
658
+ }, _options?: WriteOptions): Promise<MessageResult>;
659
+ /**
660
+ * Get gateway PDAs for a batch starting at registryIndex.
661
+ * Reads the GatewayRegistry and derives PDAs for the next `batchSize`
662
+ * active gateways.
663
+ */
664
+ getRegistryGatewayPDAs(startIndex: number, batchSize: number): Promise<Address[]>;
665
+ /** Get ALL active gateway PDAs from the registry. */
666
+ getAllRegistryGatewayPDAs(): Promise<Address[]>;
667
+ /**
668
+ * Predict the Gateway PDAs that `prescribe_epoch` will select as observers
669
+ * for `epochIndex`, mirroring the on-chain weighted-roulette selection.
670
+ *
671
+ * Returns at most `epoch_settings.prescribed_observer_count` (≤50) PDAs
672
+ * regardless of registry size — the set to pass as `gatewayAccounts` to
673
+ * {@link prescribeEpoch}. This is the size-safe replacement for
674
+ * {@link getAllRegistryGatewayPDAs} on the prescribe path (which oversupplies
675
+ * and trips `MAX_TX_ACCOUNT_LOCKS = 64` on large registries).
676
+ *
677
+ * Reads three accounts (epoch, registry, epoch settings) at the configured
678
+ * commitment so the prediction reflects live registry weights. If a selected
679
+ * gateway races out before the tx lands, `prescribeEpoch` throws
680
+ * `InvalidGatewayAccount` — re-call this and retry once.
681
+ */
682
+ getPredictedObserverPDAs(epochIndex: number): Promise<Address[]>;
683
+ /**
684
+ * Reclaim rent from the ephemeral Address Lookup Tables this signer created
685
+ * for `prescribe_epoch` (see {@link sendWithEphemeralLookupTable}). Each
686
+ * prescribe leaves a single-use table allocated (~0.0126 SOL); reclaiming
687
+ * needs a deactivate → ~513-slot cooldown → close sequence, so it can't run
688
+ * inline. Call this from a throttled/permissionless cleanup pass (cranker /
689
+ * observer) to deactivate active tables and close cooled-down ones, refunding
690
+ * the rent to the signer.
691
+ *
692
+ * Discovery reads the signer's transaction history (RPC-portable; the ALT
693
+ * program can't be enumerated via `getProgramAccounts`). The GAR + ArNS
694
+ * program IDs are passed as the entry-ownership fingerprint so only genuine
695
+ * prescribe tables are touched. Best-effort: at most `maxTables` submissions
696
+ * per call, scanning at most `scanLimit` recent signatures.
697
+ */
698
+ reclaimLookupTableRent(opts?: {
699
+ maxTables?: number;
700
+ scanLimit?: number;
701
+ }): Promise<{
702
+ deactivated: number;
703
+ closed: number;
704
+ candidates: number;
705
+ scannedSignatures: number;
706
+ }>;
707
+ /** Read and deserialize the full EpochSettings account. */
708
+ getEpochSettingsFull(): Promise<ReturnType<typeof deserializeEpochSettingsFull>>;
709
+ /**
710
+ * Submit `prescribe_epoch` using the off-chain-predicted observer set, with a
711
+ * single re-predict-and-retry on `InvalidGatewayAccount` (covers a gateway
712
+ * leaving the registry between the prediction read and the tx landing).
713
+ */
714
+ protected prescribeWithPrediction(epochIndex: number, nameRegistryAccount?: Address): Promise<MessageResult>;
715
+ /**
716
+ * Advance the epoch lifecycle by ONE on-chain action and return what it did.
717
+ *
718
+ * Stateless and idempotent: it reads `EpochSettings` + the current `Epoch`,
719
+ * determines the single next required step
720
+ * (`create` → `tally` → `prescribe` → `distribute` → `close`), submits it,
721
+ * and returns a {@link CrankEpochStepResult}. Call it repeatedly on your own
722
+ * schedule — it owns *which* on-chain action is correct and *which accounts*
723
+ * it needs; you own scheduling, logging, error classification, and any
724
+ * permissionless cleanup.
725
+ *
726
+ * Crucially, the `prescribe` leg uses {@link getPredictedObserverPDAs} (only
727
+ * the ~`prescribed_observer_count` selected Gateway PDAs), so it never trips
728
+ * `MAX_TX_ACCOUNT_LOCKS = 64` on large registries — and it re-predicts and
729
+ * retries once on `InvalidGatewayAccount`.
730
+ *
731
+ * Errors propagate to the caller (classify/retry as you see fit); the only
732
+ * internally-handled error is the prescribe `InvalidGatewayAccount` retry.
733
+ */
734
+ crankEpochStep(opts?: CrankEpochStepOptions): Promise<CrankEpochStepResult>;
735
+ /**
736
+ * One compound batch over delegations with pending rewards (≤
737
+ * {@link MAX_COMPOUND_BATCH} per tx), or `null` when none are due. Settling
738
+ * is idempotent, so this converges over a few crank steps then no-ops until
739
+ * the next epoch's distribution advances the accumulator again.
740
+ */
741
+ private maybeCompoundStep;
742
+ /**
743
+ * Roll the demand factor forward if its (wall-clock) period elapsed since the
744
+ * last stored roll, else `null`. Mirrors the on-chain period math; the roll
745
+ * itself is idempotent.
746
+ */
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;
760
+ /**
761
+ * The DemandFactor account's stored period + period-zero start (seconds) —
762
+ * the gate for {@link maybeRollDemandFactorStep}. `null` if the account
763
+ * doesn't exist (pre-genesis).
764
+ */
765
+ getDemandFactorPeriodState(): Promise<{
766
+ currentPeriod: number;
767
+ periodZeroStartTimestamp: number;
768
+ } | null>;
769
+ /**
770
+ * Read the raw epoch account data for cranker state inspection.
771
+ * Returns null if the epoch account doesn't exist yet.
772
+ */
773
+ getEpochRaw(epochIndex: number): Promise<{
774
+ tallyIndex: number;
775
+ distributionIndex: number;
776
+ weightsTallied: number;
777
+ prescriptionsDone: number;
778
+ rewardsDistributed: number;
779
+ observationsSubmitted: number;
780
+ observationsClosed: number;
781
+ activeGatewayCount: number;
782
+ endTimestamp: number;
783
+ } | null>;
784
+ /**
785
+ * Parse raw epoch account data for cranker-relevant fields.
786
+ * Offsets match the Rust Epoch zero-copy struct (repr(C)).
787
+ *
788
+ * Layout after 8-byte discriminator:
789
+ * [8 epoch_index][8 start_ts][8 end_ts][8 total_eligible][8 per_gw]
790
+ * [8 per_obs][8 reward_rate][8 weight_lo][8 weight_hi][32 hashchain]
791
+ * [4 active_gw_count][4 dist_idx][4 tally_idx]
792
+ * [1 observer_count][1 name_count][1 obs_submitted][1 rewards_dist]
793
+ * [1 weights_tallied][1 prescriptions_done][1 bump][1 obs_closed]
794
+ * [6000 failure_counts][1600 prescribed_observers]
795
+ * [1600 prescribed_observer_gateways][64 prescribed_names]
796
+ * [7 has_observed][5 _pad2]
797
+ *
798
+ * NOTE: byte +123 is `observations_closed` (NOT padding) — `close_epoch`
799
+ * reverts with EpochObservationsNotClosed until it equals obs_submitted.
800
+ */
801
+ private fetchEpochRawFields;
802
+ /**
803
+ * Batch-prune expired ArnsRecord PDAs from the NameRegistry. The caller
804
+ * supplies the eligible records as `arnsRecords` — they're appended as
805
+ * `remaining_accounts` and the on-chain handler verifies each is past
806
+ * `end_timestamp + grace_period + return_auction_duration` before closing.
807
+ * `maxNames` caps the per-tx work (u8). Submit in batches of ~10-15.
808
+ */
809
+ pruneExpiredNames(params: {
810
+ maxNames: number;
811
+ arnsRecords: string[];
812
+ }, _options?: WriteOptions): Promise<MessageResult>;
813
+ /**
814
+ * Convert a single expired-but-not-yet-returned lease into a `ReturnedName`
815
+ * (kicks off the Dutch auction). Permissionless.
816
+ */
817
+ pruneNameToReturned(params: {
818
+ name: string;
819
+ }, _options?: WriteOptions): Promise<MessageResult>;
820
+ /**
821
+ * Batch-prune expired ReturnedName PDAs (auction window elapsed). Caller
822
+ * supplies the eligible PDAs as `returnedNames`; they're appended as
823
+ * `remaining_accounts`. `maxNames` caps per-tx work (u8).
824
+ */
825
+ pruneReturnedNames(params: {
826
+ maxNames: number;
827
+ returnedNames: string[];
828
+ }, _options?: WriteOptions): Promise<MessageResult>;
829
+ /**
830
+ * Close a single expired ReservedName PDA. Permissionless after
831
+ * `expires_at`.
832
+ */
833
+ pruneExpiredReservation(params: {
834
+ name: string;
835
+ }, _options?: WriteOptions): Promise<MessageResult>;
836
+ /**
837
+ * Slash and remove a deficient gateway (`stats.failed_consecutive >=
838
+ * max_consecutive_failures`). Builds the protected exit vault for the
839
+ * post-slash min portion plus the optional excess vault for any surplus.
840
+ * The contract's `excess_withdrawal: Option<UncheckedAccount>` slot is
841
+ * always passed (PDA derived from `next_id + 1`); the handler consumes
842
+ * it only when the post-slash stake exceeds `min_operator_stake`.
843
+ * Permissionless.
844
+ */
845
+ pruneGateway(params: {
846
+ gateway: string;
847
+ }, _options?: WriteOptions): Promise<MessageResult>;
848
+ /**
849
+ * GC a `Leaving`/`Gone` gateway whose leave window has fully elapsed.
850
+ * Closes the Gateway PDA and refunds rent to the caller. Permissionless.
851
+ */
852
+ finalizeGone(params: {
853
+ gateway: string;
854
+ }, _options?: WriteOptions): Promise<MessageResult>;
855
+ /**
856
+ * Reclaim rent from an Observation PDA whose epoch has been distributed.
857
+ * Permissionless. Pass `epochIndex` and the `observer` address used as
858
+ * the Observation seed.
859
+ */
860
+ closeObservation(params: {
861
+ epochIndex: number;
862
+ observer: string;
863
+ }, _options?: WriteOptions): Promise<MessageResult>;
864
+ /**
865
+ * Close multiple Observation PDAs for one epoch in a single tx (each
866
+ * `close_observation` increments the parent Epoch's `observations_closed`).
867
+ * Permissionless; rent is refunded to the payer. Used by the crank to satisfy
868
+ * `close_epoch`'s `observations_closed == observations_submitted` precondition
869
+ * before closing a retention-aged epoch. Keep the batch small — each ix carries
870
+ * the Epoch + Observation + payer + system accounts.
871
+ */
872
+ closeObservations(params: {
873
+ epochIndex: number;
874
+ observers: string[];
875
+ }, _options?: WriteOptions): Promise<MessageResult>;
876
+ /**
877
+ * Close an empty Delegation PDA (`amount == 0`) and refund rent to the
878
+ * original delegator (NOT the caller — see GAR-016, prevents griefing).
879
+ * Permissionless.
880
+ */
881
+ closeEmptyDelegation(params: {
882
+ gateway: string;
883
+ delegator: string;
884
+ }, _options?: WriteOptions): Promise<MessageResult>;
885
+ /**
886
+ * Close a drained Withdrawal PDA (`amount == 0`) and refund rent to the
887
+ * original owner (NOT the caller). Permissionless.
888
+ */
889
+ closeDrainedWithdrawal(params: {
890
+ owner: string;
891
+ withdrawalId: number | bigint;
892
+ }, _options?: WriteOptions): Promise<MessageResult>;
893
+ }