@ar.io/sdk 3.19.0-alpha.1 → 3.19.0-alpha.11

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 (40) hide show
  1. package/README.md +155 -7
  2. package/bundles/web.bundle.min.js +88 -72
  3. package/lib/cjs/cli/cli.js +14 -1
  4. package/lib/cjs/cli/commands/antCommands.js +82 -16
  5. package/lib/cjs/cli/options.js +36 -1
  6. package/lib/cjs/cli/utils.js +25 -11
  7. package/lib/cjs/common/ant.js +452 -12
  8. package/lib/cjs/common/io.js +2 -2
  9. package/lib/cjs/constants.js +2 -1
  10. package/lib/cjs/types/ant.js +18 -0
  11. package/lib/cjs/utils/ant.js +4 -0
  12. package/lib/cjs/utils/ao.js +3 -1
  13. package/lib/cjs/utils/processes.js +1 -1
  14. package/lib/cjs/version.js +1 -1
  15. package/lib/esm/cli/cli.js +16 -3
  16. package/lib/esm/cli/commands/antCommands.js +81 -17
  17. package/lib/esm/cli/options.js +35 -0
  18. package/lib/esm/cli/utils.js +24 -11
  19. package/lib/esm/common/ant.js +453 -13
  20. package/lib/esm/common/io.js +2 -2
  21. package/lib/esm/constants.js +1 -0
  22. package/lib/esm/types/ant.js +18 -0
  23. package/lib/esm/utils/ant.js +4 -0
  24. package/lib/esm/utils/ao.js +3 -1
  25. package/lib/esm/utils/processes.js +1 -1
  26. package/lib/esm/version.js +1 -1
  27. package/lib/types/cli/commands/antCommands.d.ts +12 -0
  28. package/lib/types/cli/options.d.ts +25 -0
  29. package/lib/types/cli/types.d.ts +1 -0
  30. package/lib/types/cli/utils.d.ts +15 -2
  31. package/lib/types/common/ant.d.ts +167 -6
  32. package/lib/types/common/io.d.ts +1 -1
  33. package/lib/types/constants.d.ts +1 -0
  34. package/lib/types/types/ant.d.ts +131 -9
  35. package/lib/types/types/common.d.ts +31 -0
  36. package/lib/types/utils/ant.d.ts +4 -0
  37. package/lib/types/utils/ao.d.ts +2 -1
  38. package/lib/types/utils/processes.d.ts +1 -1
  39. package/lib/types/version.d.ts +1 -1
  40. package/package.json +3 -2
@@ -14,13 +14,15 @@
14
14
  * limitations under the License.
15
15
  */
16
16
  import { z } from 'zod';
17
+ import { ARIO_MAINNET_PROCESS_ID } from '../constants.js';
18
+ import { ANT_REGISTRY_ID } from '../constants.js';
17
19
  import { AntBalancesSchema, AntControllersSchema, AntInfoSchema, AntRecordSchema, AntRecordsSchema, AntStateSchema, } from '../types/ant.js';
18
20
  import { isProcessConfiguration, isProcessIdConfiguration, } from '../types/index.js';
19
21
  import { convertHyperBeamStateToAoANTState, isHyperBeamANTState, sortANTRecords, } from '../utils/ant.js';
20
22
  import { createAoSigner, forkANT, spawnANT } from '../utils/ao.js';
21
23
  import { parseSchemaResult } from '../utils/schema.js';
22
24
  import { ANTVersions } from './ant-versions.js';
23
- import { AOProcess, InvalidContractConfigurationError, Logger, } from './index.js';
25
+ import { AOProcess, ARIO, InvalidContractConfigurationError, Logger, } from './index.js';
24
26
  export class ANT {
25
27
  /**
26
28
  * Versions of ANTs according to the ANT registry.
@@ -40,6 +42,149 @@ export class ANT {
40
42
  * @param config
41
43
  */
42
44
  static fork = forkANT;
45
+ /**
46
+ * Upgrade an ANT by forking it to the latest version and reassigning names.
47
+ *
48
+ *
49
+ * @param config Configuration object for the upgrade process
50
+ * @returns Promise resolving to the forked process ID and successfully reassigned names
51
+ */
52
+ static async upgrade({ signer, antProcessId, reassignAffiliatedNames = true, names, arioProcessId = ARIO_MAINNET_PROCESS_ID, antRegistryId = ANT_REGISTRY_ID, ao, logger = Logger.default, skipVersionCheck = false, onSigningProgress, hyperbeamUrl, }) {
53
+ // run time check if names is not empty but reassignAffiliatedNames it true, throw
54
+ if (names !== undefined &&
55
+ names.length > 0 &&
56
+ reassignAffiliatedNames !== undefined &&
57
+ reassignAffiliatedNames !== false) {
58
+ throw new Error('Cannot reassign all affiliated names and provide specific names');
59
+ }
60
+ let namesToReassign = names !== undefined && names.length > 0 ? new Set(names) : new Set();
61
+ // use reassignAffiliatedNames if names is empty
62
+ const shouldReassignAll = names === undefined || names.length === 0
63
+ ? (reassignAffiliatedNames ?? true)
64
+ : false;
65
+ const ario = ARIO.init({
66
+ process: new AOProcess({ processId: arioProcessId, ao }),
67
+ });
68
+ const getAllAffiliatedNames = async () => {
69
+ let cursor = undefined;
70
+ let hasMore = true;
71
+ const affiliatedNames = new Set();
72
+ while (hasMore) {
73
+ const page = await ario.getArNSRecords({
74
+ filters: { processId: antProcessId },
75
+ cursor,
76
+ limit: 100,
77
+ });
78
+ page.items.forEach((r) => {
79
+ affiliatedNames.add(r.name);
80
+ });
81
+ cursor = page.nextCursor;
82
+ hasMore = page.hasMore;
83
+ }
84
+ return affiliatedNames;
85
+ };
86
+ // get all the affiliated names if reassign all affiliated names is true
87
+ if (shouldReassignAll) {
88
+ onSigningProgress?.('fetching-affiliated-names', {
89
+ arioProcessId,
90
+ antProcessId,
91
+ });
92
+ namesToReassign = await getAllAffiliatedNames();
93
+ }
94
+ else {
95
+ if (names === undefined || names.length === 0) {
96
+ throw new Error('Names are required when reassignAffiliatedNames is false.');
97
+ }
98
+ onSigningProgress?.('validating-names', {
99
+ arioProcessId,
100
+ antProcessId,
101
+ names,
102
+ });
103
+ // confirm all names are affiliated with the ANT
104
+ const allAffiliatedNames = await getAllAffiliatedNames();
105
+ if (!names.every((name) => allAffiliatedNames.has(name))) {
106
+ // find any that are not affiliated with the ANT
107
+ const notAffiliatedNames = names.filter((name) => !allAffiliatedNames.has(name));
108
+ throw new Error(`All names must be affiliated with the ANT on the provided ARIO process. The following names are not affiliated to this ANT: ${notAffiliatedNames.join(', ')}`);
109
+ }
110
+ }
111
+ // if names is empty and reassign all affiliated names is false, throw an error
112
+ if (namesToReassign.size === 0) {
113
+ throw new Error('There are no names to reassign for this ANT.');
114
+ }
115
+ const existingAntProcess = ANT.init({
116
+ process: new AOProcess({
117
+ processId: antProcessId,
118
+ ao,
119
+ logger,
120
+ }),
121
+ hyperbeamUrl,
122
+ signer,
123
+ });
124
+ if (!skipVersionCheck) {
125
+ onSigningProgress?.('checking-version', {
126
+ antProcessId,
127
+ antRegistryId,
128
+ });
129
+ const isLatestVersion = await existingAntProcess.isLatestVersion({
130
+ antRegistryId,
131
+ });
132
+ if (isLatestVersion) {
133
+ return {
134
+ forkedProcessId: antProcessId,
135
+ reassignedNames: {},
136
+ failedReassignedNames: {},
137
+ };
138
+ }
139
+ }
140
+ const forkedProcessId = await ANT.fork({
141
+ signer,
142
+ antProcessId,
143
+ ao,
144
+ logger,
145
+ antRegistryId,
146
+ onSigningProgress,
147
+ });
148
+ // we could parallelize this, but then signing progress would be harder to track
149
+ const reassignedNames = {};
150
+ const failedReassignedNames = {};
151
+ for (const name of namesToReassign) {
152
+ let reassignmentResult;
153
+ try {
154
+ onSigningProgress?.('reassigning-name', {
155
+ name,
156
+ arioProcessId,
157
+ antProcessId: forkedProcessId,
158
+ });
159
+ reassignmentResult = await existingAntProcess.reassignName({
160
+ name,
161
+ arioProcessId,
162
+ antProcessId: forkedProcessId,
163
+ });
164
+ onSigningProgress?.('successfully-reassigned-name', {
165
+ name,
166
+ arioProcessId,
167
+ antProcessId: forkedProcessId,
168
+ });
169
+ reassignedNames[name] = reassignmentResult;
170
+ }
171
+ catch (error) {
172
+ logger.error(`Failed to reassign name ${name}:`, { error });
173
+ onSigningProgress?.('failed-to-reassign-name', {
174
+ name,
175
+ arioProcessId,
176
+ antProcessId: forkedProcessId,
177
+ error,
178
+ });
179
+ // Continue with other names rather than failing completely
180
+ failedReassignedNames[name] = {
181
+ id: reassignmentResult?.id,
182
+ error,
183
+ };
184
+ }
185
+ }
186
+ return { forkedProcessId, reassignedNames, failedReassignedNames };
187
+ }
43
188
  static init(config) {
44
189
  if (config !== undefined && 'signer' in config) {
45
190
  return new AoANTWriteable(config);
@@ -50,9 +195,11 @@ export class ANT {
50
195
  export class AoANTReadable {
51
196
  process;
52
197
  processId;
53
- strict;
54
198
  hyperbeamUrl;
199
+ strict;
55
200
  checkHyperBeamPromise;
201
+ moduleId;
202
+ moduleIdPromise;
56
203
  logger = Logger.default;
57
204
  constructor(config) {
58
205
  this.strict = config.strict || false;
@@ -185,6 +332,185 @@ export class AoANTReadable {
185
332
  const info = await this.getInfo();
186
333
  return info.Logo;
187
334
  }
335
+ /**
336
+ * Gets the module ID of the current ANT process by querying its spawn transaction tags.
337
+ * Results are cached after the first successful fetch.
338
+ *
339
+ * @param graphqlUrl The GraphQL endpoint URL (defaults to Arweave's GraphQL endpoint)
340
+ * @param retries Number of retry attempts (defaults to 3)
341
+ * @returns Promise<string> The module ID used to spawn this ANT process
342
+ * @example
343
+ * ```ts
344
+ * const moduleId = await ant.getModuleId();
345
+ * console.log(`ANT was spawned with module: ${moduleId}`);
346
+ * ```
347
+ */
348
+ async getModuleId({
349
+ // TODO: we could use wayfinder for this
350
+ graphqlUrl = 'https://arweave.net/graphql', retries = 3, } = {}) {
351
+ // Return cached result if available
352
+ if (this.moduleId !== undefined) {
353
+ this.logger.debug('Returning cached module ID', {
354
+ processId: this.processId,
355
+ moduleId: this.moduleId,
356
+ });
357
+ return this.moduleId;
358
+ }
359
+ // Return existing promise if already in flight
360
+ if (this.moduleIdPromise) {
361
+ this.logger.debug('Returning in-flight module ID promise', {
362
+ processId: this.processId,
363
+ });
364
+ return this.moduleIdPromise;
365
+ }
366
+ // Create and cache the promise to prevent multiple concurrent requests
367
+ this.moduleIdPromise = this.fetchModuleId({ graphqlUrl, retries });
368
+ try {
369
+ const moduleId = await this.moduleIdPromise;
370
+ this.moduleId = moduleId;
371
+ this.logger.debug('Successfully fetched and cached module ID', {
372
+ processId: this.processId,
373
+ moduleId,
374
+ });
375
+ return moduleId;
376
+ }
377
+ finally {
378
+ // Clear the promise so future calls can retry if this one failed
379
+ this.moduleIdPromise = undefined;
380
+ }
381
+ }
382
+ /**
383
+ * Internal method to fetch the module ID from GraphQL.
384
+ *
385
+ * TODO: this could be more like get process headers/metadata and fetch additional details.
386
+ *
387
+ * It seems like module is the only relevant one, but scheduler and authority are also available.
388
+ */
389
+ async fetchModuleId({ graphqlUrl, retries, }) {
390
+ const query = JSON.stringify({
391
+ query: `
392
+ query {
393
+ transactions(
394
+ ids: ["${this.processId}"]
395
+ first: 1
396
+ ) {
397
+ edges {
398
+ node {
399
+ tags {
400
+ name
401
+ value
402
+ }
403
+ }
404
+ }
405
+ }
406
+ }
407
+ `,
408
+ });
409
+ for (let i = 0; i < retries; i++) {
410
+ try {
411
+ const response = await fetch(graphqlUrl, {
412
+ method: 'POST',
413
+ body: query,
414
+ headers: {
415
+ 'Content-Type': 'application/json',
416
+ },
417
+ signal: AbortSignal.timeout(10_000), // 10 second timeout
418
+ });
419
+ if (!response.ok) {
420
+ throw new Error(`GraphQL request failed: ${response.statusText}`);
421
+ }
422
+ const result = (await response.json());
423
+ if (result.errors) {
424
+ throw new Error(`GraphQL errors: ${result.errors.map((e) => e.message).join(', ')}`);
425
+ }
426
+ const edges = result.data?.transactions?.edges;
427
+ if (!edges || edges.length === 0) {
428
+ throw new Error(`No transaction found for process ID: ${this.processId}`);
429
+ }
430
+ const tags = edges[0].node.tags;
431
+ const moduleTag = tags.find((tag) => tag.name === 'Module');
432
+ if (!moduleTag) {
433
+ throw new Error(`No Module tag found for process ID: ${this.processId}`);
434
+ }
435
+ return moduleTag.value;
436
+ }
437
+ catch (error) {
438
+ if (i === retries - 1) {
439
+ // Final attempt failed
440
+ this.logger.error('Failed to get ANT module ID after all retries:', {
441
+ error,
442
+ });
443
+ throw new Error(`Unable to determine module ID for ANT process ${this.processId}: ${error.message}`);
444
+ }
445
+ // Exponential backoff
446
+ await new Promise((resolve) => setTimeout(resolve, Math.pow(2, i) * 1000));
447
+ }
448
+ }
449
+ throw new Error(`Unexpected error getting module ID for process ${this.processId}`);
450
+ }
451
+ /**
452
+ * Gets the version string of the current ANT by matching its module ID
453
+ * with versions from the ANT registry.
454
+ *
455
+ * @param antRegistryId The ANT registry process ID (defaults to mainnet registry)
456
+ * @param graphqlUrl The GraphQL endpoint URL for getModuleId (defaults to Arweave's GraphQL endpoint)
457
+ * @param retries Number of retry attempts for getModuleId (defaults to 3)
458
+ * @returns Promise<string> The version string (e.g., "1.0.15") or "unknown" if not found
459
+ * @example
460
+ * ```ts
461
+ * const version = await ant.getVersion();
462
+ * console.log(`ANT is running version: ${version}`);
463
+ * ```
464
+ */
465
+ async getVersion({ antRegistryId = ANT_REGISTRY_ID, graphqlUrl = 'https://arweave.net/graphql', retries = 3, } = {}) {
466
+ // Get the current ANT's module ID
467
+ const currentModuleId = await this.getModuleId({ graphqlUrl, retries });
468
+ // Get all versions from the ANT registry
469
+ const antVersions = ANTVersions.init({
470
+ processId: antRegistryId,
471
+ });
472
+ const versions = await antVersions.getANTVersions();
473
+ // Find the version that matches our module ID
474
+ for (const [version, versionInfo] of Object.entries(versions)) {
475
+ if (versionInfo.moduleId === currentModuleId) {
476
+ this.logger.debug('Found matching ANT version', {
477
+ processId: this.processId,
478
+ moduleId: currentModuleId,
479
+ version,
480
+ });
481
+ return version;
482
+ }
483
+ }
484
+ const versionForModuleId = Object.entries(versions)
485
+ .map(([version, versionInfo]) => ({
486
+ version,
487
+ ...versionInfo,
488
+ }))
489
+ .find((obj) => obj.moduleId === currentModuleId);
490
+ return versionForModuleId?.version ?? 'unknown';
491
+ }
492
+ /**
493
+ * Checks if the current ANT version is the latest according to the ANT registry.
494
+ *
495
+ * @param antRegistryId Optional ANT registry process ID. Defaults to mainnet ANT registry.
496
+ * @param graphqlUrl Optional GraphQL endpoint. Defaults to https://arweave.net/graphql.
497
+ * @param retries Optional number of retries for fetching module ID. Defaults to 3.
498
+ * @returns {Promise<boolean>} True if current ANT version is the latest, false otherwise.
499
+ */
500
+ async isLatestVersion({ antRegistryId = ANT_REGISTRY_ID, graphqlUrl = 'https://arweave.net/graphql', retries = 3, } = {}) {
501
+ // Get the current ANT's version
502
+ const currentVersion = await this.getVersion({
503
+ antRegistryId,
504
+ graphqlUrl,
505
+ retries,
506
+ });
507
+ // Get all versions from the ANT registry
508
+ const antVersions = ANTVersions.init({
509
+ processId: antRegistryId,
510
+ });
511
+ const latestVersion = await antVersions.getLatestANTVersion();
512
+ return currentVersion === latestVersion.version;
513
+ }
188
514
  /**
189
515
  * @param undername @type {string} The domain name.
190
516
  * @returns {Promise<ANTRecord>} The record of the undername domain.
@@ -432,17 +758,31 @@ export class AoANTWriteable extends AoANTReadable {
432
758
  * @param undername @type {string} The record you want to set the transactionId and ttlSeconds of.
433
759
  * @param transactionId @type {string} The transactionId of the record.
434
760
  * @param ttlSeconds @type {number} The time to live of the record.
761
+ * @param owner @type {string} Optional owner address for the record.
762
+ * @param displayName @type {string} Optional display name for the record.
763
+ * @param logo @type {string} Optional logo transaction ID for the record.
764
+ * @param description @type {string} Optional description for the record.
765
+ * @param keywords @type {string[]} Optional keywords array for the record.
435
766
  * @returns {Promise<AoMessageResult>} The result of the interaction.
436
767
  */
437
- async setRecord({ undername, transactionId, ttlSeconds }, options) {
768
+ async setRecord({ undername, transactionId, ttlSeconds, owner, displayName, logo, description, keywords, }, options) {
769
+ const tags = [
770
+ ...(options?.tags ?? []),
771
+ { name: 'Action', value: 'Set-Record' },
772
+ { name: 'Sub-Domain', value: undername },
773
+ { name: 'Transaction-Id', value: transactionId },
774
+ { name: 'TTL-Seconds', value: ttlSeconds.toString() },
775
+ { name: 'Record-Owner', value: owner },
776
+ { name: 'Display-Name', value: displayName },
777
+ { name: 'Logo', value: logo },
778
+ { name: 'Description', value: description },
779
+ {
780
+ name: 'Keywords',
781
+ value: keywords ? JSON.stringify(keywords) : undefined,
782
+ },
783
+ ].filter((tag) => tag.value !== undefined);
438
784
  return this.process.send({
439
- tags: [
440
- ...(options?.tags ?? []),
441
- { name: 'Action', value: 'Set-Record' },
442
- { name: 'Sub-Domain', value: undername },
443
- { name: 'Transaction-Id', value: transactionId },
444
- { name: 'TTL-Seconds', value: ttlSeconds.toString() },
445
- ],
785
+ tags,
446
786
  signer: this.signer,
447
787
  });
448
788
  }
@@ -451,17 +791,27 @@ export class AoANTWriteable extends AoANTReadable {
451
791
  *
452
792
  * @param transactionId @type {string} The transactionId of the record.
453
793
  * @param ttlSeconds @type {number} The time to live of the record.
794
+ * @param owner @type {string} Optional owner address for the record.
795
+ * @param displayName @type {string} Optional display name for the record.
796
+ * @param logo @type {string} Optional logo transaction ID for the record.
797
+ * @param description @type {string} Optional description for the record.
798
+ * @param keywords @type {string[]} Optional keywords array for the record.
454
799
  * @returns {Promise<AoMessageResult>} The result of the interaction.
455
800
  * @example
456
801
  * ```ts
457
802
  * ant.setBaseNameRecord({ transactionId: "432l1cy0aksiL_x9M359faGzM_yjralacHIUo8_nQXM", ttlSeconds: 100 }); // ardrive.ar.io will resolve to the provided transaction id and be cached for 100 seconds by clients
458
803
  * ```
459
804
  */
460
- async setBaseNameRecord({ transactionId, ttlSeconds }, options) {
805
+ async setBaseNameRecord({ transactionId, ttlSeconds, owner, displayName, logo, description, keywords, }, options) {
461
806
  return this.setRecord({
462
807
  transactionId,
463
808
  ttlSeconds,
464
809
  undername: '@',
810
+ owner,
811
+ displayName,
812
+ logo,
813
+ description,
814
+ keywords,
465
815
  }, options);
466
816
  }
467
817
  /**
@@ -470,17 +820,27 @@ export class AoANTWriteable extends AoANTReadable {
470
820
  * @param undername @type {string} The undername of the ANT.
471
821
  * @param transactionId @type {string} The transactionId of the record.
472
822
  * @param ttlSeconds @type {number} The time to live of the record.
823
+ * @param owner @type {string} Optional owner address for the record.
824
+ * @param displayName @type {string} Optional display name for the record.
825
+ * @param logo @type {string} Optional logo transaction ID for the record.
826
+ * @param description @type {string} Optional description for the record.
827
+ * @param keywords @type {string[]} Optional keywords array for the record.
473
828
  * @returns {Promise<AoMessageResult>} The result of the interaction.
474
829
  * @example
475
830
  * ```ts
476
831
  * ant.setUndernameRecord({ undername: "dapp", transactionId: "432l1cy0aksiL_x9M359faGzM_yjralacHIUo8_nQXM", ttlSeconds: 100 }); // dapp_ardrive.ar.io will resolve to the provided transaction id and be cached for 100 seconds by clients
477
832
  * ```
478
833
  */
479
- async setUndernameRecord({ undername, transactionId, ttlSeconds }, options) {
834
+ async setUndernameRecord({ undername, transactionId, ttlSeconds, owner, displayName, logo, description, keywords, }, options) {
480
835
  return this.setRecord({
481
836
  undername,
482
837
  transactionId,
483
838
  ttlSeconds,
839
+ owner,
840
+ displayName,
841
+ logo,
842
+ description,
843
+ keywords,
484
844
  }, options);
485
845
  }
486
846
  /**
@@ -703,7 +1063,9 @@ export class AoANTWriteable extends AoANTReadable {
703
1063
  * ant.removePrimaryNames({ names: ["ardrive", "dapp_ardrive"], arioProcessId: ARIO_MAINNET_PROCESS_ID, notifyOwners: true }); // removes the primary names and associated wallet addresses assigned to "ardrive" and "dapp_ardrive"
704
1064
  * ```
705
1065
  */
706
- async removePrimaryNames({ names, arioProcessId, notifyOwners = false, }, options) {
1066
+ async removePrimaryNames({ names, arioProcessId,
1067
+ // TODO: remove this param, its not used on the ANT contract
1068
+ notifyOwners = false, }, options) {
707
1069
  return this.process.send({
708
1070
  tags: [
709
1071
  ...(options?.tags ?? []),
@@ -716,4 +1078,82 @@ export class AoANTWriteable extends AoANTReadable {
716
1078
  signer: this.signer,
717
1079
  });
718
1080
  }
1081
+ /**
1082
+ * Upgrade this ANT by forking it to the latest version and reassigning names.
1083
+ *
1084
+ * This is a convenience method that calls the static ANT.upgrade() method
1085
+ * using this instance's process ID and signer.
1086
+ *
1087
+ * current version with latest ANT registry version and skip if already up to date.
1088
+ *
1089
+ * @param names @type {string[]} The ArNS names to reassign to the upgraded ANT.
1090
+ * @param arioProcessId @type {string} The processId of the ARIO contract.
1091
+ * @param antRegistryId @type {string} Optional ANT registry ID.
1092
+ * @param onSigningProgress Progress callback function.
1093
+ * @returns {Promise} The upgrade results.
1094
+ * @example
1095
+ * ```ts
1096
+ * const result = await ant.upgrade({
1097
+ * names: ["example", "test"],
1098
+ * arioProcessId: ARIO_MAINNET_PROCESS_ID
1099
+ * });
1100
+ * console.log(`Upgraded to process: ${result.forkedProcessId}`);
1101
+ * ```
1102
+ */
1103
+ async upgrade(params) {
1104
+ const { names, reassignAffiliatedNames, arioProcessId, antRegistryId, skipVersionCheck, onSigningProgress, } = params ?? {};
1105
+ // Determine if we should reassign all names or specific names
1106
+ const shouldReassignAll = names === undefined || names.length === 0
1107
+ ? (reassignAffiliatedNames ?? true)
1108
+ : false;
1109
+ if (shouldReassignAll) {
1110
+ return ANT.upgrade({
1111
+ signer: this.signer,
1112
+ antProcessId: this.processId,
1113
+ ao: this.process.ao,
1114
+ hyperbeamUrl: this.hyperbeamUrl?.toString(),
1115
+ reassignAffiliatedNames: true,
1116
+ arioProcessId: arioProcessId,
1117
+ antRegistryId: antRegistryId,
1118
+ onSigningProgress: onSigningProgress,
1119
+ skipVersionCheck: skipVersionCheck,
1120
+ });
1121
+ }
1122
+ else {
1123
+ return ANT.upgrade({
1124
+ signer: this.signer,
1125
+ antProcessId: this.processId,
1126
+ ao: this.process.ao,
1127
+ hyperbeamUrl: this.hyperbeamUrl?.toString(),
1128
+ names: names,
1129
+ reassignAffiliatedNames: false,
1130
+ arioProcessId: arioProcessId,
1131
+ antRegistryId: antRegistryId,
1132
+ onSigningProgress: onSigningProgress,
1133
+ skipVersionCheck: skipVersionCheck,
1134
+ });
1135
+ }
1136
+ }
1137
+ /**
1138
+ * Transfers ownership of a specific record (undername) to another address. This allows delegation of control for individual records within an ANT while maintaining the ANT owner's ultimate authority.
1139
+ *
1140
+ * @param undername @type {string} The subdomain/record whose ownership you want to transfer.
1141
+ * @param recipient @type {string} The address of the new owner for this record.
1142
+ * @returns {Promise<AoMessageResult>} The result of the interaction.
1143
+ * @example
1144
+ * ```ts
1145
+ * ant.transferRecord({ undername: "alice", recipient: "new-owner-address-123..." }); // transfers ownership of the "alice" record to the new owner
1146
+ * ```
1147
+ */
1148
+ async transferRecord({ undername, recipient, }, options) {
1149
+ return this.process.send({
1150
+ tags: [
1151
+ ...(options?.tags ?? []),
1152
+ { name: 'Action', value: 'Transfer-Record' },
1153
+ { name: 'Sub-Domain', value: undername },
1154
+ { name: 'Recipient', value: recipient },
1155
+ ],
1156
+ signer: this.signer,
1157
+ });
1158
+ }
719
1159
  }
@@ -680,12 +680,12 @@ export class ARIOReadable {
680
680
  * @returns {Promise<AoArNSNameData[]>} The ARNS names associated with the address
681
681
  */
682
682
  async getArNSRecordsForAddress(params) {
683
- const { antRegistryId = ANT_REGISTRY_ID, address } = params;
683
+ const { antRegistryProcessId = ANT_REGISTRY_ID, address } = params;
684
684
  const antRegistry = ANTRegistry.init({
685
685
  hyperbeamUrl: this.hyperbeamUrl,
686
686
  process: new AOProcess({
687
687
  ao: this.process.ao,
688
- processId: antRegistryId,
688
+ processId: antRegistryProcessId,
689
689
  }),
690
690
  });
691
691
  // Note: there could be a race condition here if the ACL changes during pagination requests, resulting in different results from the `getArNSRecords`.
@@ -21,6 +21,7 @@ export const ARIO_DEVNET_PROCESS_ID = 'GaQrvEMKBpkjofgnBi_B3IgIDmY_XYelVLB6GcRGr
21
21
  export const arioDevnetProcessId = ARIO_DEVNET_PROCESS_ID;
22
22
  export const ARIO_TESTNET_PROCESS_ID = 'agYcCFJtrMG6cqMuZfskIkFTGvUPddICmtQSBIoPdiA';
23
23
  export const ARIO_MAINNET_PROCESS_ID = 'qNvAoz0TgcH7DMg8BCVn8jF32QH5L6T29VjHxhHqqGE';
24
+ export const ANT_REGISTRY_TESTNET_ID = 'RR0vheYqtsKuJCWh6xj0beE35tjaEug5cejMw9n2aa8';
24
25
  export const ANT_REGISTRY_ID = 'i_le_yKKPVstLTDSmkHRqf-wYphMnwB9OhleiTgMkWc';
25
26
  export const MARIO_PER_ARIO = 1_000_000;
26
27
  /**
@@ -51,6 +51,23 @@ export const AntRecordSchema = z.object({
51
51
  transactionId: ArweaveTxIdSchema.describe('The Target ID of the undername'),
52
52
  ttlSeconds: z.number(),
53
53
  priority: z.number().optional(),
54
+ owner: AOAddressSchema.describe('The owner address of the record').optional(),
55
+ displayName: z
56
+ .string()
57
+ .max(61)
58
+ .describe('Display name of the record (max 61 chars)')
59
+ .optional(),
60
+ logo: ArweaveTxIdSchema.describe('Logo transaction ID for the record').optional(),
61
+ description: z
62
+ .string()
63
+ .max(512)
64
+ .describe('Description of the record (max 512 chars)')
65
+ .optional(),
66
+ keywords: z
67
+ .array(z.string().max(32))
68
+ .max(16)
69
+ .describe('Keywords array (max 16, each max 32 chars)')
70
+ .optional(),
54
71
  });
55
72
  export const AntRecordsSchema = z.record(z.string(), AntRecordSchema);
56
73
  export const AntControllersSchema = z.array(AOAddressSchema.describe('Controller address'));
@@ -116,6 +133,7 @@ export const AntWriteHandlers = [
116
133
  'reassignName',
117
134
  'approvePrimaryName',
118
135
  'removePrimaryNames',
136
+ 'transferRecordOwnership',
119
137
  ];
120
138
  export const AntHandlerNames = [...AntReadHandlers, ...AntWriteHandlers];
121
139
  export const AntHandlersSchema = z
@@ -37,6 +37,9 @@ export const sortANTRecords = (antRecords) => {
37
37
  // now that they are sorted, add the index to each record - this is their position in the sorted list and is used to enforce undername limits
38
38
  return Object.fromEntries(sortedEntries.map(([a, aRecord], index) => [a, { ...aRecord, index }]));
39
39
  };
40
+ /**
41
+ * @deprecated - this is no longer necessary because HyperBeam now uses the AoANTState type
42
+ */
40
43
  export const isHyperBeamANTState = (state) => {
41
44
  return ('name' in state &&
42
45
  'ticker' in state &&
@@ -54,6 +57,7 @@ export const isHyperBeamANTState = (state) => {
54
57
  /**
55
58
  * Convert HyperBeam serialized ANT state to backwards compatible format.
56
59
  *
60
+ * @deprecated - this is no longer necessary because HyperBeam now uses the AOANTState type
57
61
  * @param state - The HyperBeam serialized ANT state.
58
62
  */
59
63
  export const convertHyperBeamStateToAoANTState = (initialState) => {
@@ -211,7 +211,7 @@ export async function spawnANT({ signer, module, ao = connect({
211
211
  }
212
212
  return processId;
213
213
  }
214
- export async function forkANT({ signer, antProcessId, logger = Logger.default, ao, antRegistryId = ANT_REGISTRY_ID, onSigningProgress = (name, payload) => {
214
+ export async function forkANT({ signer, antProcessId, logger = Logger.default, ao, moduleId, antRegistryId = ANT_REGISTRY_ID, onSigningProgress = (name, payload) => {
215
215
  logger.debug('Forking ANT', { name, payload });
216
216
  }, }) {
217
217
  // get the state of the current ANT and use it to spawn a new ANT
@@ -229,7 +229,9 @@ export async function forkANT({ signer, antProcessId, logger = Logger.default, a
229
229
  const forkedProcessId = await spawnANT({
230
230
  signer,
231
231
  antRegistryId,
232
+ ao,
232
233
  logger,
234
+ module: moduleId,
233
235
  onSigningProgress,
234
236
  state: {
235
237
  owner: state.Owner,
@@ -23,7 +23,7 @@ import { ARIO } from '../common/io.js';
23
23
  import { Logger } from '../common/logger.js';
24
24
  import { ARIO_MAINNET_PROCESS_ID } from '../constants.js';
25
25
  /**
26
- * @beta This API is in beta and may change in the future.
26
+ * @deprecated Use getArNSRecordsForAddress instead
27
27
  */
28
28
  export const getANTProcessesOwnedByWallet = async ({ address, registry = ANTRegistry.init(), }) => {
29
29
  const res = await registry.accessControlList({ address });
@@ -14,4 +14,4 @@
14
14
  * limitations under the License.
15
15
  */
16
16
  // AUTOMATICALLY GENERATED FILE - DO NOT TOUCH
17
- export const version = '3.19.0-alpha.1';
17
+ export const version = '3.19.0-alpha.11';