@ar.io/sdk 3.19.0-alpha.1 → 3.19.0-alpha.10
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.
- package/README.md +52 -0
- package/bundles/web.bundle.min.js +88 -72
- package/lib/cjs/cli/cli.js +6 -0
- package/lib/cjs/cli/commands/antCommands.js +50 -0
- package/lib/cjs/cli/options.js +12 -1
- package/lib/cjs/cli/utils.js +11 -11
- package/lib/cjs/common/ant.js +383 -1
- package/lib/cjs/common/io.js +2 -2
- package/lib/cjs/constants.js +2 -1
- package/lib/cjs/utils/ant.js +4 -0
- package/lib/cjs/utils/ao.js +3 -1
- package/lib/cjs/utils/processes.js +1 -1
- package/lib/cjs/version.js +1 -1
- package/lib/esm/cli/cli.js +8 -2
- package/lib/esm/cli/commands/antCommands.js +50 -1
- package/lib/esm/cli/options.js +11 -0
- package/lib/esm/cli/utils.js +11 -11
- package/lib/esm/common/ant.js +384 -2
- package/lib/esm/common/io.js +2 -2
- package/lib/esm/constants.js +1 -0
- package/lib/esm/utils/ant.js +4 -0
- package/lib/esm/utils/ao.js +3 -1
- package/lib/esm/utils/processes.js +1 -1
- package/lib/esm/version.js +1 -1
- package/lib/types/cli/commands/antCommands.d.ts +8 -0
- package/lib/types/cli/options.d.ts +9 -0
- package/lib/types/cli/utils.d.ts +2 -2
- package/lib/types/common/ant.d.ts +134 -3
- package/lib/types/common/io.d.ts +1 -1
- package/lib/types/constants.d.ts +1 -0
- package/lib/types/types/ant.d.ts +34 -1
- package/lib/types/types/common.d.ts +31 -0
- package/lib/types/utils/ant.d.ts +4 -0
- package/lib/types/utils/ao.d.ts +2 -1
- package/lib/types/utils/processes.d.ts +1 -1
- package/lib/types/version.d.ts +1 -1
- package/package.json +1 -1
package/lib/esm/cli/cli.js
CHANGED
|
@@ -19,12 +19,12 @@ import { program } from 'commander';
|
|
|
19
19
|
import { AOProcess, spawnANT } from '../node/index.js';
|
|
20
20
|
import { mARIOToken } from '../types/token.js';
|
|
21
21
|
import { version } from '../version.js';
|
|
22
|
-
import { setAntBaseNameCLICommand, setAntRecordCLICommand, } from './commands/antCommands.js';
|
|
22
|
+
import { setAntBaseNameCLICommand, setAntRecordCLICommand, upgradeAntCLICommand, } from './commands/antCommands.js';
|
|
23
23
|
import { buyRecordCLICommand, extendLeaseCLICommand, increaseUndernameLimitCLICommand, requestPrimaryNameCLICommand, setPrimaryNameCLICommand, upgradeRecordCLICommand, } from './commands/arnsPurchaseCommands.js';
|
|
24
24
|
import { cancelWithdrawal, decreaseDelegateStake, decreaseOperatorStake, delegateStake, increaseOperatorStake, instantWithdrawal, joinNetwork, leaveNetwork, redelegateStake, saveObservations, updateGatewaySettings, } from './commands/gatewayWriteCommands.js';
|
|
25
25
|
import { getAllGatewayVaults, getAllowedDelegates, getArNSRecord, getArNSReservedName, getArNSReturnedName, getCostDetails, getDelegations, getEpoch, getGateway, getGatewayDelegates, getGatewayVaults, getPrescribedNames, getPrescribedObservers, getPrimaryName, getTokenCost, getVault, listAllDelegatesCLICommand, listAntsForAddress, listArNSRecords, listArNSRecordsForAddress, listArNSReservedNames, listArNSReturnedNames, listGateways, resolveArNSName, } from './commands/readCommands.js';
|
|
26
26
|
import { createVaultCLICommand, extendVaultCLICommand, increaseVaultCLICommand, revokeVaultCLICommand, transferCLICommand, vaultedTransferCLICommand, } from './commands/transfer.js';
|
|
27
|
-
import { addressAndVaultIdOptions, antStateOptions, arnsPurchaseOptions, buyRecordOptions, decreaseDelegateStakeOptions, delegateStakeOptions, epochOptions, getVaultOptions, globalOptions, joinNetworkOptions, operatorStakeOptions, optionMap, paginationAddressOptions, paginationOptions, redelegateStakeOptions, setAntBaseNameOptions, setAntUndernameOptions, tokenCostOptions, transferOptions, updateGatewaySettingsOptions, vaultedTransferOptions, writeActionOptions, } from './options.js';
|
|
27
|
+
import { addressAndVaultIdOptions, antStateOptions, arnsPurchaseOptions, buyRecordOptions, decreaseDelegateStakeOptions, delegateStakeOptions, epochOptions, getVaultOptions, globalOptions, joinNetworkOptions, operatorStakeOptions, optionMap, paginationAddressOptions, paginationOptions, redelegateStakeOptions, setAntBaseNameOptions, setAntUndernameOptions, tokenCostOptions, transferOptions, updateGatewaySettingsOptions, upgradeAntOptions, vaultedTransferOptions, writeActionOptions, } from './options.js';
|
|
28
28
|
import { applyOptions, arioProcessIdFromOptions, assertConfirmationPrompt, customTagsFromOptions, epochInputFromOptions, formatARIOWithCommas, getANTStateFromOptions, getLoggerFromOptions, makeCommand, paginationParamsFromOptions, readANTFromOptions, readARIOFromOptions, requiredAddressFromOptions, requiredAoSignerFromOptions, requiredProcessIdFromOptions, requiredStringArrayFromOptions, requiredStringFromOptions, writeANTFromOptions, } from './utils.js';
|
|
29
29
|
applyOptions(program
|
|
30
30
|
.name('ar.io')
|
|
@@ -632,6 +632,12 @@ makeCommand({
|
|
|
632
632
|
options: setAntUndernameOptions,
|
|
633
633
|
action: setAntRecordCLICommand,
|
|
634
634
|
});
|
|
635
|
+
makeCommand({
|
|
636
|
+
name: 'upgrade-ant',
|
|
637
|
+
description: 'Upgrade an ANT by forking it to the latest version and reassigning names',
|
|
638
|
+
options: upgradeAntOptions,
|
|
639
|
+
action: upgradeAntCLICommand,
|
|
640
|
+
});
|
|
635
641
|
makeCommand({
|
|
636
642
|
name: 'set-ant-ticker',
|
|
637
643
|
description: 'Set the ticker of an ANT process',
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { assertConfirmationPrompt, customTagsFromOptions, defaultTtlSecondsCLI, requiredStringFromOptions, writeANTFromOptions, } from '../utils.js';
|
|
1
|
+
import { arioProcessIdFromOptions, assertConfirmationPrompt, booleanFromOptions, customTagsFromOptions, defaultTtlSecondsCLI, readARIOFromOptions, requiredStringFromOptions, stringArrayFromOptions, writeANTFromOptions, } from '../utils.js';
|
|
2
2
|
/** @deprecated -- use set-ant-base-name and set-ant-undername */
|
|
3
3
|
export async function setAntRecordCLICommand(o) {
|
|
4
4
|
const ttlSeconds = +(o.ttlSeconds ?? defaultTtlSecondsCLI);
|
|
@@ -40,3 +40,52 @@ export async function setAntUndernameCLICommand(o) {
|
|
|
40
40
|
ttlSeconds,
|
|
41
41
|
}, customTagsFromOptions(o));
|
|
42
42
|
}
|
|
43
|
+
export async function upgradeAntCLICommand(o) {
|
|
44
|
+
const writeAnt = writeANTFromOptions(o);
|
|
45
|
+
const arioProcessId = arioProcessIdFromOptions(o);
|
|
46
|
+
const ario = readARIOFromOptions(o);
|
|
47
|
+
const reassignAffiliatedNames = booleanFromOptions(o, 'reassignAffiliatedNames');
|
|
48
|
+
const names = stringArrayFromOptions(o, 'names') || [];
|
|
49
|
+
if (reassignAffiliatedNames) {
|
|
50
|
+
// Fetch all ArNS records that point to this ANT process
|
|
51
|
+
const allRecords = await ario.getArNSRecords({
|
|
52
|
+
filters: {
|
|
53
|
+
processId: writeAnt.processId,
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
// Filter records that belong to this ANT
|
|
57
|
+
const affiliatedNames = allRecords.items.map((record) => record.name);
|
|
58
|
+
names.push(...affiliatedNames);
|
|
59
|
+
}
|
|
60
|
+
if (names.length === 0) {
|
|
61
|
+
throw new Error('No names to reassign');
|
|
62
|
+
}
|
|
63
|
+
if (!o.skipConfirmation) {
|
|
64
|
+
await assertConfirmationPrompt(`Upgrade all names affiliated with this ANT on ARIO process?\n` +
|
|
65
|
+
`ARIO Process ID: ${arioProcessId}\n` +
|
|
66
|
+
`ANT Process ID: ${writeAnt.processId}\n` +
|
|
67
|
+
`Names that will be reassigned (${names.length}): ${names.join(', ')}`, o);
|
|
68
|
+
}
|
|
69
|
+
const result = reassignAffiliatedNames
|
|
70
|
+
? await writeANTFromOptions(o).upgrade({
|
|
71
|
+
reassignAffiliatedNames,
|
|
72
|
+
arioProcessId,
|
|
73
|
+
})
|
|
74
|
+
: await writeANTFromOptions(o).upgrade({
|
|
75
|
+
names,
|
|
76
|
+
arioProcessId,
|
|
77
|
+
});
|
|
78
|
+
// Serialize error objects for JSON compatibility
|
|
79
|
+
const serializedFailedReassignedNames = {};
|
|
80
|
+
for (const [name, failure] of Object.entries(result.failedReassignedNames)) {
|
|
81
|
+
serializedFailedReassignedNames[name] = {
|
|
82
|
+
id: failure.id,
|
|
83
|
+
error: failure.error.message,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
return {
|
|
87
|
+
forkedProcessId: result.forkedProcessId,
|
|
88
|
+
reassignedNames: result.reassignedNames,
|
|
89
|
+
failedReassignedNames: serializedFailedReassignedNames,
|
|
90
|
+
};
|
|
91
|
+
}
|
package/lib/esm/cli/options.js
CHANGED
|
@@ -300,6 +300,11 @@ export const optionMap = {
|
|
|
300
300
|
alias: '--referrer <referrer>',
|
|
301
301
|
description: 'The referrer for ArNS purchase tracking',
|
|
302
302
|
},
|
|
303
|
+
reassignAffiliatedNames: {
|
|
304
|
+
alias: '--reassign-affiliated-names',
|
|
305
|
+
description: 'Reassign all affiliated names to the new process',
|
|
306
|
+
type: 'boolean',
|
|
307
|
+
},
|
|
303
308
|
};
|
|
304
309
|
export const walletOptions = [
|
|
305
310
|
optionMap.walletFile,
|
|
@@ -419,3 +424,9 @@ export const setAntUndernameOptions = [
|
|
|
419
424
|
...setAntBaseNameOptions,
|
|
420
425
|
optionMap.undername,
|
|
421
426
|
];
|
|
427
|
+
export const upgradeAntOptions = [
|
|
428
|
+
optionMap.processId,
|
|
429
|
+
optionMap.names,
|
|
430
|
+
optionMap.reassignAffiliatedNames,
|
|
431
|
+
...writeActionOptions,
|
|
432
|
+
];
|
package/lib/esm/cli/utils.js
CHANGED
|
@@ -18,7 +18,7 @@ import { connect } from '@permaweb/aoconnect';
|
|
|
18
18
|
import { program } from 'commander';
|
|
19
19
|
import { readFileSync } from 'fs';
|
|
20
20
|
import prompts from 'prompts';
|
|
21
|
-
import { ANT, ANTRegistry, ANT_REGISTRY_ID, AOProcess, ARIO, ARIOToken, ARIO_DEVNET_PROCESS_ID, ARIO_MAINNET_PROCESS_ID, ARIO_TESTNET_PROCESS_ID, ArweaveSigner, Logger, createAoSigner, fromB64Url, fundFromOptions, initANTStateForAddress, isValidFundFrom, isValidIntent, mARIOToken, sha256B64Url, validIntents, } from '../node/index.js';
|
|
21
|
+
import { ANT, ANTRegistry, ANT_REGISTRY_ID, ANT_REGISTRY_TESTNET_ID, AOProcess, ARIO, ARIOToken, ARIO_DEVNET_PROCESS_ID, ARIO_MAINNET_PROCESS_ID, ARIO_TESTNET_PROCESS_ID, ArweaveSigner, Logger, createAoSigner, fromB64Url, fundFromOptions, initANTStateForAddress, isValidFundFrom, isValidIntent, mARIOToken, sha256B64Url, validIntents, } from '../node/index.js';
|
|
22
22
|
import { globalOptions } from './options.js';
|
|
23
23
|
export const defaultTtlSecondsCLI = 3600;
|
|
24
24
|
export function stringifyJsonForCLIDisplay(json) {
|
|
@@ -78,6 +78,15 @@ export function arioProcessIdFromOptions({ arioProcessId, devnet, testnet, }) {
|
|
|
78
78
|
}
|
|
79
79
|
return ARIO_MAINNET_PROCESS_ID;
|
|
80
80
|
}
|
|
81
|
+
export function antRegistryIdFromOptions({ antRegistryProcessId, testnet, }) {
|
|
82
|
+
if (antRegistryProcessId !== undefined) {
|
|
83
|
+
return antRegistryProcessId;
|
|
84
|
+
}
|
|
85
|
+
if (testnet) {
|
|
86
|
+
return ANT_REGISTRY_TESTNET_ID;
|
|
87
|
+
}
|
|
88
|
+
return ANT_REGISTRY_ID;
|
|
89
|
+
}
|
|
81
90
|
function walletFromOptions({ privateKey, walletFile, }) {
|
|
82
91
|
if (privateKey !== undefined) {
|
|
83
92
|
return JSON.parse(privateKey);
|
|
@@ -125,18 +134,9 @@ export function readARIOFromOptions(options) {
|
|
|
125
134
|
paymentUrl: options.paymentUrl,
|
|
126
135
|
});
|
|
127
136
|
}
|
|
128
|
-
export function ANTRegistryProcessFromOptions(options) {
|
|
129
|
-
return new AOProcess({
|
|
130
|
-
processId: options.antRegistryProcessId ?? ANT_REGISTRY_ID,
|
|
131
|
-
ao: connect({
|
|
132
|
-
MODE: 'legacy',
|
|
133
|
-
CU_URL: options.cuUrl,
|
|
134
|
-
}),
|
|
135
|
-
});
|
|
136
|
-
}
|
|
137
137
|
export function readANTRegistryFromOptions(options) {
|
|
138
138
|
return ANTRegistry.init({
|
|
139
|
-
process:
|
|
139
|
+
process: aoProcessFromOptions(options),
|
|
140
140
|
hyperbeamUrl: options.hyperbeamUrl,
|
|
141
141
|
});
|
|
142
142
|
}
|
package/lib/esm/common/ant.js
CHANGED
|
@@ -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.
|
|
@@ -716,4 +1042,60 @@ export class AoANTWriteable extends AoANTReadable {
|
|
|
716
1042
|
signer: this.signer,
|
|
717
1043
|
});
|
|
718
1044
|
}
|
|
1045
|
+
/**
|
|
1046
|
+
* Upgrade this ANT by forking it to the latest version and reassigning names.
|
|
1047
|
+
*
|
|
1048
|
+
* This is a convenience method that calls the static ANT.upgrade() method
|
|
1049
|
+
* using this instance's process ID and signer.
|
|
1050
|
+
*
|
|
1051
|
+
* current version with latest ANT registry version and skip if already up to date.
|
|
1052
|
+
*
|
|
1053
|
+
* @param names @type {string[]} The ArNS names to reassign to the upgraded ANT.
|
|
1054
|
+
* @param arioProcessId @type {string} The processId of the ARIO contract.
|
|
1055
|
+
* @param antRegistryId @type {string} Optional ANT registry ID.
|
|
1056
|
+
* @param onSigningProgress Progress callback function.
|
|
1057
|
+
* @returns {Promise} The upgrade results.
|
|
1058
|
+
* @example
|
|
1059
|
+
* ```ts
|
|
1060
|
+
* const result = await ant.upgrade({
|
|
1061
|
+
* names: ["example", "test"],
|
|
1062
|
+
* arioProcessId: ARIO_MAINNET_PROCESS_ID
|
|
1063
|
+
* });
|
|
1064
|
+
* console.log(`Upgraded to process: ${result.forkedProcessId}`);
|
|
1065
|
+
* ```
|
|
1066
|
+
*/
|
|
1067
|
+
async upgrade(params) {
|
|
1068
|
+
const { names, reassignAffiliatedNames, arioProcessId, antRegistryId, skipVersionCheck, onSigningProgress, } = params ?? {};
|
|
1069
|
+
// Determine if we should reassign all names or specific names
|
|
1070
|
+
const shouldReassignAll = names === undefined || names.length === 0
|
|
1071
|
+
? (reassignAffiliatedNames ?? true)
|
|
1072
|
+
: false;
|
|
1073
|
+
if (shouldReassignAll) {
|
|
1074
|
+
return ANT.upgrade({
|
|
1075
|
+
signer: this.signer,
|
|
1076
|
+
antProcessId: this.processId,
|
|
1077
|
+
ao: this.process.ao,
|
|
1078
|
+
hyperbeamUrl: this.hyperbeamUrl?.toString(),
|
|
1079
|
+
reassignAffiliatedNames: true,
|
|
1080
|
+
arioProcessId: arioProcessId,
|
|
1081
|
+
antRegistryId: antRegistryId,
|
|
1082
|
+
onSigningProgress: onSigningProgress,
|
|
1083
|
+
skipVersionCheck: skipVersionCheck,
|
|
1084
|
+
});
|
|
1085
|
+
}
|
|
1086
|
+
else {
|
|
1087
|
+
return ANT.upgrade({
|
|
1088
|
+
signer: this.signer,
|
|
1089
|
+
antProcessId: this.processId,
|
|
1090
|
+
ao: this.process.ao,
|
|
1091
|
+
hyperbeamUrl: this.hyperbeamUrl?.toString(),
|
|
1092
|
+
names: names,
|
|
1093
|
+
reassignAffiliatedNames: false,
|
|
1094
|
+
arioProcessId: arioProcessId,
|
|
1095
|
+
antRegistryId: antRegistryId,
|
|
1096
|
+
onSigningProgress: onSigningProgress,
|
|
1097
|
+
skipVersionCheck: skipVersionCheck,
|
|
1098
|
+
});
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
719
1101
|
}
|
package/lib/esm/common/io.js
CHANGED
|
@@ -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 {
|
|
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:
|
|
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`.
|
package/lib/esm/constants.js
CHANGED
|
@@ -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
|
/**
|
package/lib/esm/utils/ant.js
CHANGED
|
@@ -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) => {
|
package/lib/esm/utils/ao.js
CHANGED
|
@@ -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
|
-
* @
|
|
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 });
|
package/lib/esm/version.js
CHANGED
|
@@ -19,3 +19,11 @@ import { CLIWriteOptionsFromAoAntParams } from '../types.js';
|
|
|
19
19
|
export declare function setAntRecordCLICommand(o: CLIWriteOptionsFromAoAntParams<AoANTSetUndernameRecordParams>): Promise<import("../../types/common.js").AoMessageResult<Record<string, string | number | boolean | null>>>;
|
|
20
20
|
export declare function setAntBaseNameCLICommand(o: CLIWriteOptionsFromAoAntParams<AoANTSetBaseNameRecordParams>): Promise<import("../../types/common.js").AoMessageResult<Record<string, string | number | boolean | null>>>;
|
|
21
21
|
export declare function setAntUndernameCLICommand(o: CLIWriteOptionsFromAoAntParams<AoANTSetUndernameRecordParams>): Promise<import("../../types/common.js").AoMessageResult<Record<string, string | number | boolean | null>>>;
|
|
22
|
+
export declare function upgradeAntCLICommand(o: CLIWriteOptionsFromAoAntParams<Record<string, unknown>>): Promise<{
|
|
23
|
+
forkedProcessId: string;
|
|
24
|
+
reassignedNames: Record<string, import("../../types/common.js").AoMessageResult>;
|
|
25
|
+
failedReassignedNames: Record<string, {
|
|
26
|
+
id?: string;
|
|
27
|
+
error: string;
|
|
28
|
+
}>;
|
|
29
|
+
}>;
|