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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (169) hide show
  1. package/README.md +757 -589
  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 +227 -0
  6. package/lib/esm/cli/commands/gatewayWriteCommands.js +140 -23
  7. package/lib/esm/cli/commands/pruneCommands.js +154 -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 +303 -175
  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 +17 -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 +66 -258
  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
@@ -14,10 +14,10 @@
14
14
  * limitations under the License.
15
15
  */
16
16
  import prompts from 'prompts';
17
- import { mARIOToken } from '../../node/index.js';
17
+ import { mARIOToken } from '../../types/token.js';
18
18
  import { assertConfirmationPrompt, assertEnoughMARIOBalance, customTagsFromOptions, formatARIOWithCommas, gatewaySettingsFromOptions, redelegateParamsFromOptions, requiredAddressFromOptions, requiredMARIOFromOptions, requiredStringArrayFromOptions, requiredStringFromOptions, requiredTargetAndQuantityFromOptions, stringifyJsonForCLIDisplay, writeARIOFromOptions, } from '../utils.js';
19
19
  export async function joinNetwork(options) {
20
- const { ario, signerAddress } = writeARIOFromOptions(options);
20
+ const { ario, signerAddress } = await writeARIOFromOptions(options);
21
21
  const mARIOQuantity = requiredMARIOFromOptions(options, 'operatorStake');
22
22
  const settings = {
23
23
  ...gatewaySettingsFromOptions(options),
@@ -50,7 +50,7 @@ export async function joinNetwork(options) {
50
50
  return output;
51
51
  }
52
52
  export async function updateGatewaySettings(options) {
53
- const { ario, signerAddress } = writeARIOFromOptions(options);
53
+ const { ario, signerAddress } = await writeARIOFromOptions(options);
54
54
  const gatewaySettings = gatewaySettingsFromOptions(options);
55
55
  if (Object.keys(gatewaySettings).length === 0) {
56
56
  // TODO: The contract accepts empty Update-Gateway-Settings actions, but we'll throw in the CLI for now
@@ -66,7 +66,7 @@ export async function updateGatewaySettings(options) {
66
66
  return output;
67
67
  }
68
68
  export async function leaveNetwork(options) {
69
- const { ario, signerAddress } = writeARIOFromOptions(options);
69
+ const { ario, signerAddress } = await writeARIOFromOptions(options);
70
70
  if (!options.skipConfirmation) {
71
71
  const gateway = await ario.getGateway({ address: signerAddress });
72
72
  await assertConfirmationPrompt('Gateway Details:\n\n' +
@@ -74,13 +74,13 @@ export async function leaveNetwork(options) {
74
74
  '\n\n' +
75
75
  'Are you sure you want to leave the AR.IO network?', options);
76
76
  }
77
- return writeARIOFromOptions(options).ario.leaveNetwork(customTagsFromOptions(options));
77
+ return ario.leaveNetwork(customTagsFromOptions(options));
78
78
  }
79
79
  export async function saveObservations(o) {
80
80
  const failedGateways = requiredStringArrayFromOptions(o, 'failedGateways');
81
81
  const reportTxId = requiredStringFromOptions(o, 'transactionId');
82
82
  await assertConfirmationPrompt(`You are about to save the following failed gateways to the AR.IO network:\n\n${failedGateways.join('\n')}\n\nTransaction ID: ${reportTxId}\n\nAre you sure?`, o);
83
- return writeARIOFromOptions(o).ario.saveObservations({
83
+ return (await writeARIOFromOptions(o)).ario.saveObservations({
84
84
  failedGateways: requiredStringArrayFromOptions(o, 'failedGateways'),
85
85
  reportTxId: requiredStringFromOptions(o, 'transactionId'),
86
86
  }, customTagsFromOptions(o));
@@ -88,23 +88,47 @@ export async function saveObservations(o) {
88
88
  export async function increaseOperatorStake(o) {
89
89
  const increaseQty = requiredMARIOFromOptions(o, 'operatorStake');
90
90
  await assertConfirmationPrompt(`You are about to increase your operator stake by ${formatARIOWithCommas(increaseQty.toARIO())} ARIO\nAre you sure?`, o);
91
- return writeARIOFromOptions(o).ario.increaseOperatorStake({
91
+ return (await writeARIOFromOptions(o)).ario.increaseOperatorStake({
92
92
  increaseQty,
93
93
  }, customTagsFromOptions(o));
94
94
  }
95
95
  export async function decreaseOperatorStake(o) {
96
+ const { ario, signerAddress } = await writeARIOFromOptions(o);
96
97
  const decreaseQty = requiredMARIOFromOptions(o, 'operatorStake');
97
- // TODO: Can assert stake is sufficient for action, and new target stake meets contract minimum
98
+ if (!o.skipConfirmation) {
99
+ const gateway = await ario.getGateway({ address: signerAddress });
100
+ if (gateway === undefined) {
101
+ throw new Error(`No gateway found for address ${signerAddress}. You must be a gateway operator to decrease stake.`);
102
+ }
103
+ const settings = await ario.getGatewayRegistrySettings();
104
+ const currentStake = gateway.operatorStake;
105
+ const remaining = currentStake - decreaseQty.valueOf();
106
+ if (remaining < 0) {
107
+ throw new Error(`Insufficient operator stake. Current: ${formatARIOWithCommas(new mARIOToken(currentStake).toARIO())} ARIO, requested decrease: ${formatARIOWithCommas(decreaseQty.toARIO())} ARIO`);
108
+ }
109
+ if (remaining > 0 && remaining < settings.operators.minStake) {
110
+ throw new Error(`Remaining stake (${formatARIOWithCommas(new mARIOToken(remaining).toARIO())} ARIO) would be below minimum (${formatARIOWithCommas(new mARIOToken(settings.operators.minStake).toARIO())} ARIO). Decrease to exactly 0 (after calling leave-network) or keep above the minimum.`);
111
+ }
112
+ }
98
113
  await assertConfirmationPrompt(`You are about to decrease your operator stake by ${formatARIOWithCommas(decreaseQty.toARIO())} ARIO\nAre you sure?`, o);
99
- return writeARIOFromOptions(o).ario.decreaseOperatorStake({
114
+ return ario.decreaseOperatorStake({
100
115
  decreaseQty,
101
116
  }, customTagsFromOptions(o));
102
117
  }
118
+ export async function claimWithdrawal(o) {
119
+ const vaultId = requiredStringFromOptions(o, 'vaultId');
120
+ await assertConfirmationPrompt(`You are about to claim matured withdrawal vault ${vaultId}. Tokens will be transferred to your wallet.\nAre you sure?`, o);
121
+ // claimWithdrawal is Solana-only — surface it through the SolanaARIOWriteable
122
+ // class rather than the shared ARIOWrite interface (see the comment in
123
+ // src/types/io.ts above syncAttributes).
124
+ const { ario } = await writeARIOFromOptions(o);
125
+ return ario.claimWithdrawal({ withdrawalId: vaultId }, customTagsFromOptions(o));
126
+ }
103
127
  export async function instantWithdrawal(o) {
104
128
  const vaultId = requiredStringFromOptions(o, 'vaultId');
105
129
  const gatewayAddress = requiredAddressFromOptions(o);
106
130
  await assertConfirmationPrompt(`You are about to instantly withdraw from vault ${vaultId} for with gateway address ${gatewayAddress}\nAre you sure?`, o);
107
- return writeARIOFromOptions(o).ario.instantWithdrawal({
131
+ return (await writeARIOFromOptions(o)).ario.instantWithdrawal({
108
132
  vaultId,
109
133
  gatewayAddress,
110
134
  }, customTagsFromOptions(o));
@@ -113,13 +137,13 @@ export async function cancelWithdrawal(o) {
113
137
  const vaultId = requiredStringFromOptions(o, 'vaultId');
114
138
  const gatewayAddress = requiredAddressFromOptions(o);
115
139
  await assertConfirmationPrompt(`You are about to cancel the pending withdrawal from vault ${vaultId} for with gateway address ${gatewayAddress}\nAre you sure?`, o);
116
- return writeARIOFromOptions(o).ario.cancelWithdrawal({
140
+ return (await writeARIOFromOptions(o)).ario.cancelWithdrawal({
117
141
  vaultId,
118
142
  gatewayAddress,
119
143
  }, customTagsFromOptions(o));
120
144
  }
121
145
  export async function delegateStake(options) {
122
- const { ario, signerAddress } = writeARIOFromOptions(options);
146
+ const { ario, signerAddress } = await writeARIOFromOptions(options);
123
147
  const { target, arioQuantity } = requiredTargetAndQuantityFromOptions(options);
124
148
  const mARIOQuantity = arioQuantity.toMARIO();
125
149
  if (!options.skipConfirmation) {
@@ -134,8 +158,59 @@ export async function delegateStake(options) {
134
158
  if (targetGateway.settings.allowDelegatedStaking === false) {
135
159
  throw new Error(`Gateway does not allow delegated staking: ${target}`);
136
160
  }
137
- // TODO: could get allow list and assert doesn't exist or user is on it
138
- // TODO: could read from contract to get current delegated stake if there is none, get contract minimum delegated stake. Then see if the new stake value will satisfy minimum delegated stake for both the target gateway settings min delegate stake and contract min delegated amounts
161
+ // Check allowlist if gateway restricts delegation. `allowDelegatedStaking`
162
+ // is `boolean | 'allowlist'`; the SDK maps `allowlist_enabled = true` on
163
+ // Solana to the `'allowlist'` variant (see deserialize.ts).
164
+ //
165
+ // `getGatewayDelegateAllowList` returns `PaginationResult<WalletAddress>`,
166
+ // so we walk pages by cursor — the previous shape (plain `string[]`) was
167
+ // never the actual return type and made the membership check dead code.
168
+ //
169
+ // Note: the on-chain delegate handler also lets you bypass the allowlist
170
+ // if you already have stake > 0 with this gateway (delegate.rs). We
171
+ // can't easily check that client-side, so this preflight surfaces the
172
+ // most common case (new-delegator-not-on-list) but falls through any
173
+ // unexpected error to let the on-chain check arbitrate.
174
+ if (targetGateway.settings.allowDelegatedStaking === 'allowlist') {
175
+ try {
176
+ let cursor;
177
+ let onAllowlist = false;
178
+ let allowlistHasEntries = false;
179
+ do {
180
+ const page = await ario.getGatewayDelegateAllowList({
181
+ address: target,
182
+ limit: 1_000,
183
+ cursor,
184
+ });
185
+ if (page.items.length > 0)
186
+ allowlistHasEntries = true;
187
+ if (page.items.includes(signerAddress)) {
188
+ onAllowlist = true;
189
+ break;
190
+ }
191
+ cursor = page.nextCursor;
192
+ } while (cursor);
193
+ if (allowlistHasEntries && !onAllowlist) {
194
+ throw new Error(`You (${signerAddress}) are not on the delegation allowlist for gateway ${target}.`);
195
+ }
196
+ }
197
+ catch (e) {
198
+ // Re-throw our own "not on allowlist" error; swallow anything else
199
+ // so the on-chain check can produce the canonical failure.
200
+ if (e instanceof Error &&
201
+ e.message.includes('not on the delegation allowlist')) {
202
+ throw e;
203
+ }
204
+ }
205
+ }
206
+ // Validate minimum delegation amount
207
+ const settings = await ario.getGatewayRegistrySettings();
208
+ const contractMinDelegation = settings.delegates.minStake;
209
+ const gatewayMinDelegation = targetGateway.settings.minDelegatedStake ?? contractMinDelegation;
210
+ const effectiveMin = Math.max(contractMinDelegation, gatewayMinDelegation);
211
+ if (mARIOQuantity.valueOf() < effectiveMin) {
212
+ throw new Error(`Delegation amount (${formatARIOWithCommas(arioQuantity)} ARIO) is below minimum (${formatARIOWithCommas(new mARIOToken(effectiveMin).toARIO())} ARIO).`);
213
+ }
139
214
  const { confirm } = await prompts({
140
215
  type: 'confirm',
141
216
  name: 'confirm',
@@ -157,13 +232,39 @@ export async function delegateStake(options) {
157
232
  return output;
158
233
  }
159
234
  export async function decreaseDelegateStake(options) {
160
- const ario = writeARIOFromOptions(options).ario;
235
+ const { ario, signerAddress } = await writeARIOFromOptions(options);
161
236
  const { target, arioQuantity } = requiredTargetAndQuantityFromOptions(options);
162
237
  const instant = options.instant ?? false;
163
- // TODO: Could assert sender is a delegate with enough stake to decrease
164
- // TODO: Could assert new target stake meets contract and target gateway minimums
165
- // TODO: Could present confirmation prompt with any fee for instant withdrawal (50% of the stake is put back into protocol??)
166
- await assertConfirmationPrompt(`Are you sure you'd like to decrease delegated stake of ${formatARIOWithCommas(arioQuantity)} ARIO on gateway ${target}?`, options);
238
+ if (!options.skipConfirmation) {
239
+ // Verify the target gateway exists and look up delegation
240
+ const targetGateway = await ario.getGateway({ address: target });
241
+ if (targetGateway === undefined) {
242
+ throw new Error(`Gateway not found: ${target}`);
243
+ }
244
+ // Find delegation to this gateway
245
+ const delegations = await ario.getDelegations({ address: signerAddress });
246
+ const delegation = delegations.items.find((d) => d.gatewayAddress === target && d.type === 'stake');
247
+ if (!delegation) {
248
+ throw new Error(`No active delegation found for you on gateway ${target}.`);
249
+ }
250
+ if (arioQuantity.toMARIO().valueOf() > delegation.balance) {
251
+ throw new Error(`Cannot decrease by ${formatARIOWithCommas(arioQuantity)} ARIO — you only have ${formatARIOWithCommas(new mARIOToken(delegation.balance).toARIO())} ARIO delegated.`);
252
+ }
253
+ const remaining = delegation.balance - arioQuantity.toMARIO().valueOf();
254
+ if (remaining > 0) {
255
+ const registrySettings = await ario.getGatewayRegistrySettings();
256
+ const contractMin = registrySettings.delegates.minStake;
257
+ if (remaining < contractMin) {
258
+ throw new Error(`Remaining delegation (${formatARIOWithCommas(new mARIOToken(remaining).toARIO())} ARIO) would be below minimum (${formatARIOWithCommas(new mARIOToken(contractMin).toARIO())} ARIO). Decrease to exactly 0 or keep above the minimum.`);
259
+ }
260
+ }
261
+ }
262
+ if (instant) {
263
+ await assertConfirmationPrompt(`WARNING: Instant withdrawal incurs a penalty.\nAre you sure you'd like to instantly decrease delegated stake of ${formatARIOWithCommas(arioQuantity)} ARIO on gateway ${target}?`, options);
264
+ }
265
+ else {
266
+ await assertConfirmationPrompt(`Are you sure you'd like to decrease delegated stake of ${formatARIOWithCommas(arioQuantity)} ARIO on gateway ${target}?`, options);
267
+ }
167
268
  const result = await ario.decreaseDelegateStake({
168
269
  target,
169
270
  decreaseQty: arioQuantity.toMARIO(),
@@ -177,11 +278,27 @@ export async function decreaseDelegateStake(options) {
177
278
  return output;
178
279
  }
179
280
  export async function redelegateStake(options) {
180
- const ario = writeARIOFromOptions(options).ario;
281
+ const { ario, signerAddress } = await writeARIOFromOptions(options);
181
282
  const params = redelegateParamsFromOptions(options);
182
- // TODO: Could assert target gateway exists
183
- // TODO: Could do assertion on source has enough stake to redelegate
184
- // TODO: Could do assertions on source/target min delegate stakes are met
283
+ if (!options.skipConfirmation) {
284
+ const targetGateway = await ario.getGateway({ address: params.target });
285
+ if (targetGateway === undefined) {
286
+ throw new Error(`Target gateway not found: ${params.target}`);
287
+ }
288
+ if (targetGateway.settings.allowDelegatedStaking === false) {
289
+ throw new Error(`Target gateway ${params.target} does not allow delegated staking.`);
290
+ }
291
+ const sourceGateway = await ario.getGateway({ address: params.source });
292
+ if (sourceGateway === undefined) {
293
+ throw new Error(`Source gateway not found: ${params.source}`);
294
+ }
295
+ const delegations = await ario.getDelegations({ address: signerAddress });
296
+ const delegation = delegations.items.find((d) => d.gatewayAddress === params.source && d.type === 'stake');
297
+ if (!delegation || delegation.balance < params.stakeQty.valueOf()) {
298
+ const available = delegation?.balance ?? 0;
299
+ throw new Error(`Insufficient delegated stake on source gateway ${params.source}. Available: ${formatARIOWithCommas(new mARIOToken(available).toARIO())} ARIO, requested: ${formatARIOWithCommas(params.stakeQty.toARIO())} ARIO`);
300
+ }
301
+ }
185
302
  await assertConfirmationPrompt(`Are you sure you'd like to redelegate stake of ${formatARIOWithCommas(params.stakeQty.toARIO())} ARIO from ${params.source} to ${params.target}?`, options);
186
303
  const result = await ario.redelegateStake(params);
187
304
  const output = {
@@ -0,0 +1,154 @@
1
+ import { assertConfirmationPrompt, requiredStringFromOptions, writeARIOFromOptions, } from '../utils.js';
2
+ async function getSolanaWriter(o) {
3
+ const { ario } = await writeARIOFromOptions(o);
4
+ return ario;
5
+ }
6
+ function parseMaxNames(o, fallbackCount) {
7
+ const raw = o.max;
8
+ if (raw === undefined) {
9
+ // When an explicit name list is supplied, derive the u8 batch size from
10
+ // it (capped at the on-chain max of 255) so callers don't have to also
11
+ // pass --max. Discovery paths (no explicit list) still require --max.
12
+ if (fallbackCount !== undefined && fallbackCount > 0) {
13
+ return Math.min(fallbackCount, 255);
14
+ }
15
+ throw new Error('--max <count> is required (u8 batch size, 1-255)');
16
+ }
17
+ const n = Number(raw);
18
+ if (!Number.isInteger(n) || n < 1 || n > 255) {
19
+ throw new Error(`--max must be an integer 1-255 (got ${raw})`);
20
+ }
21
+ return n;
22
+ }
23
+ // =========================================
24
+ // ArNS prune
25
+ // =========================================
26
+ export async function pruneExpiredNamesCLICommand(o) {
27
+ const max = parseMaxNames(o, o.arnsRecords?.length);
28
+ const ario = await getSolanaWriter(o);
29
+ // If `--arns-records` wasn't provided, discover them via the readable's
30
+ // helper. Cap at `max` so we never overshoot the ix's u8 batch parameter.
31
+ let records = o.arnsRecords ?? [];
32
+ if (records.length === 0) {
33
+ const now = Math.floor(Date.now() / 1000);
34
+ const expired = await ario.getExpiredArnsRecords(now);
35
+ records = expired.slice(0, max).map((r) => r.pubkey);
36
+ if (records.length === 0) {
37
+ return { message: 'No expired ArnsRecords to prune' };
38
+ }
39
+ }
40
+ else {
41
+ records = records.slice(0, max);
42
+ }
43
+ await assertConfirmationPrompt(`Prune ${records.length} expired ArnsRecord(s)?`, o);
44
+ return ario.pruneExpiredNames({ maxNames: max, arnsRecords: records });
45
+ }
46
+ export async function pruneNameToReturnedCLICommand(o) {
47
+ const name = requiredStringFromOptions(o, 'name');
48
+ const ario = await getSolanaWriter(o);
49
+ await assertConfirmationPrompt(`Convert expired lease for "${name}" to a ReturnedName (Dutch auction)?`, o);
50
+ return ario.pruneNameToReturned({ name });
51
+ }
52
+ export async function pruneReturnedNamesCLICommand(o) {
53
+ const max = parseMaxNames(o, o.returnedNames?.length);
54
+ const ario = await getSolanaWriter(o);
55
+ let returned = o.returnedNames ?? [];
56
+ if (returned.length === 0) {
57
+ const now = Math.floor(Date.now() / 1000);
58
+ const expired = await ario.getExpiredReturnedNames(now);
59
+ returned = expired.slice(0, max).map((r) => r.pubkey);
60
+ if (returned.length === 0) {
61
+ return { message: 'No expired ReturnedNames to prune' };
62
+ }
63
+ }
64
+ else {
65
+ returned = returned.slice(0, max);
66
+ }
67
+ await assertConfirmationPrompt(`Prune ${returned.length} expired ReturnedName(s)?`, o);
68
+ return ario.pruneReturnedNames({
69
+ maxNames: max,
70
+ returnedNames: returned,
71
+ });
72
+ }
73
+ export async function pruneExpiredReservationCLICommand(o) {
74
+ const name = requiredStringFromOptions(o, 'name');
75
+ const ario = await getSolanaWriter(o);
76
+ await assertConfirmationPrompt(`Close expired reservation for "${name}"?`, o);
77
+ return ario.pruneExpiredReservation({ name });
78
+ }
79
+ // =========================================
80
+ // Gateway prune
81
+ // =========================================
82
+ export async function pruneGatewayCLICommand(o) {
83
+ const gateway = requiredStringFromOptions(o, 'gateway');
84
+ const ario = await getSolanaWriter(o);
85
+ await assertConfirmationPrompt(`Slash + remove deficient gateway ${gateway}?`, o);
86
+ return ario.pruneGateway({ gateway });
87
+ }
88
+ export async function finalizeGoneCLICommand(o) {
89
+ const gateway = requiredStringFromOptions(o, 'gateway');
90
+ const ario = await getSolanaWriter(o);
91
+ await assertConfirmationPrompt(`Finalize-GC departed gateway ${gateway} (reclaim PDA rent)?`, o);
92
+ return ario.finalizeGone({ gateway });
93
+ }
94
+ // =========================================
95
+ // Rent reclaim
96
+ // =========================================
97
+ export async function closeObservationCLICommand(o) {
98
+ const epochIndexStr = requiredStringFromOptions(o, 'epochIndex');
99
+ const observer = requiredStringFromOptions(o, 'observer');
100
+ const epochIndex = Number(epochIndexStr);
101
+ if (!Number.isInteger(epochIndex) || epochIndex < 0) {
102
+ throw new Error(`--epoch-index must be a non-negative integer (got ${epochIndexStr})`);
103
+ }
104
+ const ario = await getSolanaWriter(o);
105
+ await assertConfirmationPrompt(`Close Observation PDA (epoch ${epochIndex}, observer ${observer})?`, o);
106
+ return ario.closeObservation({ epochIndex, observer });
107
+ }
108
+ export async function closeEmptyDelegationCLICommand(o) {
109
+ const gateway = requiredStringFromOptions(o, 'gateway');
110
+ const delegator = requiredStringFromOptions(o, 'delegator');
111
+ const ario = await getSolanaWriter(o);
112
+ await assertConfirmationPrompt(`Close empty Delegation PDA (gateway=${gateway}, delegator=${delegator})?`, o);
113
+ return ario.closeEmptyDelegation({ gateway, delegator });
114
+ }
115
+ export async function closeDrainedWithdrawalCLICommand(o) {
116
+ const owner = requiredStringFromOptions(o, 'owner');
117
+ const withdrawalIdStr = requiredStringFromOptions(o, 'withdrawalId');
118
+ // Validate before BigInt() — `BigInt('0xff')` and `BigInt(' 1 ')` succeed
119
+ // and `BigInt('abc')` throws an opaque SyntaxError. Restrict to the u64
120
+ // decimal form the on-chain seed encoder expects so the CLI fails with a
121
+ // clear message instead of a downstream parser error.
122
+ if (!/^\d+$/.test(withdrawalIdStr)) {
123
+ throw new Error(`--withdrawal-id must be a non-negative decimal integer (got "${withdrawalIdStr}")`);
124
+ }
125
+ const withdrawalId = BigInt(withdrawalIdStr);
126
+ const ario = await getSolanaWriter(o);
127
+ await assertConfirmationPrompt(`Close drained Withdrawal PDA (owner=${owner}, id=${withdrawalIdStr})?`, o);
128
+ return ario.closeDrainedWithdrawal({ owner, withdrawalId });
129
+ }
130
+ // =========================================
131
+ // Vault + primary-name request
132
+ // =========================================
133
+ export async function releaseVaultCLICommand(o) {
134
+ // The on-chain handler requires `owner: Signer` — the SDK uses the
135
+ // configured signer as the owner. The `--owner` flag is accepted as
136
+ // documentation but ignored unless it matches the signer; fail loud if
137
+ // it doesn't, so users don't think they can release someone else's vault.
138
+ const { ario: arioWrite, signerAddress } = await writeARIOFromOptions(o);
139
+ const ario = arioWrite;
140
+ if (o.owner && o.owner !== signerAddress) {
141
+ throw new Error(`release-vault: --owner ${o.owner} does not match signer ${signerAddress}. ` +
142
+ `release_vault is owner-signed; use the owner's wallet to call it.`);
143
+ }
144
+ const vaultIdStr = requiredStringFromOptions(o, 'vaultId');
145
+ const vaultId = vaultIdStr;
146
+ await assertConfirmationPrompt(`Release expired vault id=${vaultIdStr} (transfer tokens back to owner)?`, o);
147
+ return ario.releaseVault({ vaultId });
148
+ }
149
+ export async function closeExpiredRequestCLICommand(o) {
150
+ const initiator = requiredStringFromOptions(o, 'initiator');
151
+ const ario = await getSolanaWriter(o);
152
+ await assertConfirmationPrompt(`Close expired primary-name request from ${initiator}?`, o);
153
+ return ario.closeExpiredRequest({ initiator });
154
+ }
@@ -67,7 +67,6 @@ export async function listArNSRecordsForAddress(o) {
67
67
  const names = await readARIOFromOptions(o).getArNSRecordsForAddress({
68
68
  ...paginationParams,
69
69
  address,
70
- antRegistryProcessId: o.antRegistryProcessId,
71
70
  });
72
71
  return names.items.length ? names : { message: 'No names found' };
73
72
  }
@@ -165,6 +164,18 @@ export async function getAllGatewayVaults(o) {
165
164
  message: `No vaults found`,
166
165
  };
167
166
  }
167
+ export async function getWithdrawals(o) {
168
+ const address = requiredAddressFromOptions(o);
169
+ const result = await readARIOFromOptions(o).getWithdrawals({
170
+ address,
171
+ ...paginationParamsFromOptions(o),
172
+ });
173
+ return result.items?.length
174
+ ? result
175
+ : {
176
+ message: `No pending withdrawals found for address ${address}`,
177
+ };
178
+ }
168
179
  export async function getVault(o) {
169
180
  return readARIOFromOptions(o)
170
181
  .getVault({
@@ -182,8 +193,16 @@ export async function resolveArNSName(o) {
182
193
  }
183
194
  export async function listAntsForAddress(o) {
184
195
  const address = requiredAddressFromOptions(o);
185
- const result = await readANTRegistryFromOptions(o).accessControlList({
196
+ // Both AO and Solana backends expose `accessControlList({ address })`.
197
+ // Solana is backed by the paginated per-user ACL (ADR-012): a head
198
+ // `AclConfig` PDA at ["acl_config", user] plus N `AclPage` PDAs at
199
+ // ["acl_page", user, page_idx_le]. The ario-ant program maintains them
200
+ // alongside `initialize`, `add_controller`, `remove_controller`,
201
+ // `transfer_record`, and the marketplace reconcile path.
202
+ const registry = await readANTRegistryFromOptions(o);
203
+ const result = await registry.accessControlList({
186
204
  address,
187
205
  });
188
- return result ?? { message: `No ANTs found for address ${address}` };
206
+ const hasAny = result && (result.Owned.length > 0 || result.Controlled.length > 0);
207
+ return hasAny ? result : { message: `No ANTs found for address ${address}` };
189
208
  }
@@ -2,7 +2,7 @@ import { mARIOToken } from '../../types/token.js';
2
2
  import { assertEnoughMARIOBalance, assertLockLengthInRange, confirmationPrompt, customTagsFromOptions, formatARIOWithCommas, formatMARIOToARIOWithCommas, requiredMARIOFromOptions, requiredPositiveIntegerFromOptions, requiredStringFromOptions, requiredTargetAndQuantityFromOptions, writeARIOFromOptions, } from '../utils.js';
3
3
  export async function transferCLICommand(options) {
4
4
  const { target, arioQuantity } = requiredTargetAndQuantityFromOptions(options);
5
- const { ario, signerAddress } = writeARIOFromOptions(options);
5
+ const { ario, signerAddress } = await writeARIOFromOptions(options);
6
6
  if (!options.skipConfirmation) {
7
7
  await assertEnoughMARIOBalance({
8
8
  ario,
@@ -28,7 +28,7 @@ export async function transferCLICommand(options) {
28
28
  export async function vaultedTransferCLICommand(o) {
29
29
  const mARIOQuantity = requiredMARIOFromOptions(o, 'quantity');
30
30
  const recipient = requiredStringFromOptions(o, 'recipient');
31
- const { ario, signerAddress } = writeARIOFromOptions(o);
31
+ const { ario, signerAddress } = await writeARIOFromOptions(o);
32
32
  const lockLengthMs = requiredPositiveIntegerFromOptions(o, 'lockLengthMs');
33
33
  assertLockLengthInRange(lockLengthMs);
34
34
  if (!o.skipConfirmation) {
@@ -56,7 +56,7 @@ export async function vaultedTransferCLICommand(o) {
56
56
  return output;
57
57
  }
58
58
  export async function revokeVaultCLICommand(o) {
59
- const { ario, signerAddress } = writeARIOFromOptions(o);
59
+ const { ario, signerAddress } = await writeARIOFromOptions(o);
60
60
  const vaultId = requiredStringFromOptions(o, 'vaultId');
61
61
  const recipient = requiredStringFromOptions(o, 'recipient');
62
62
  if (!o.skipConfirmation) {
@@ -79,7 +79,7 @@ export async function revokeVaultCLICommand(o) {
79
79
  }
80
80
  export async function createVaultCLICommand(o) {
81
81
  const mARIOQuantity = requiredMARIOFromOptions(o, 'quantity');
82
- const { ario, signerAddress } = writeARIOFromOptions(o);
82
+ const { ario, signerAddress } = await writeARIOFromOptions(o);
83
83
  const lockLengthMs = requiredPositiveIntegerFromOptions(o, 'lockLengthMs');
84
84
  assertLockLengthInRange(lockLengthMs);
85
85
  if (!o.skipConfirmation) {
@@ -105,7 +105,7 @@ export async function createVaultCLICommand(o) {
105
105
  return output;
106
106
  }
107
107
  export async function extendVaultCLICommand(o) {
108
- const { ario, signerAddress } = writeARIOFromOptions(o);
108
+ const { ario, signerAddress } = await writeARIOFromOptions(o);
109
109
  const vaultId = requiredStringFromOptions(o, 'vaultId');
110
110
  const extendLengthMs = requiredPositiveIntegerFromOptions(o, 'extendLengthMs');
111
111
  assertLockLengthInRange(extendLengthMs, false);
@@ -129,7 +129,7 @@ export async function extendVaultCLICommand(o) {
129
129
  }
130
130
  export async function increaseVaultCLICommand(o) {
131
131
  const mARIOQuantity = requiredMARIOFromOptions(o, 'quantity');
132
- const { ario, signerAddress } = writeARIOFromOptions(o);
132
+ const { ario, signerAddress } = await writeARIOFromOptions(o);
133
133
  const vaultId = requiredStringFromOptions(o, 'vaultId');
134
134
  if (!o.skipConfirmation) {
135
135
  const vault = await ario.getVault({ vaultId, address: signerAddress });